Apache中BeanUtils的copyProperties的源码分析

来源:互联网 发布:淘宝热点运动专营店 编辑:程序博客网 时间:2024/06/06 04:23

最近在使用Apache的BeanUtils的时候,看了一下源代码。对静态方法copyProperties的一些分析


当我们调用BeanUtils.copyProperties(dest,orig)方法的时候.

1.程序会根据环境上下文创建一个BeanUtilsBean对象,再根据该对象调用其方法copyProperties(dest,orig),源代码如下.

BeanUtilsBean.getInstance().copyProperties(dest, orig);
    public static BeanUtilsBean getInstance() {        return (BeanUtilsBean) BEANS_BY_CLASSLOADER.get();    }
    /**      * Contains <code>BeanUtilsBean</code> instances indexed by context classloader.     */    private static final ContextClassLoaderLocal             BEANS_BY_CLASSLOADER = new ContextClassLoaderLocal() {                        // Creates the default instance used when the context classloader is unavailable                        protected Object initialValue() {                            return new BeanUtilsBean();                        }                    };

2.创建BeanUtilsBean对象的时候,会创建用于设置属性值的new ConvertUtilsBean()对象,和用于访问属性new PropertyUtilsBean()对象,代码如下.当在BeanUtilsBean对象中注册好了这两个对象后,才会开始使用copyProperties方法.

    public BeanUtilsBean() {        this(new ConvertUtilsBean(), new PropertyUtilsBean());    }
3.注意,在正式使用我们的copyProperties方法的时候,我们的程序会先在new ConvertUtilsBean()对象的时候做一些事情,什么事情呢?就是注册转换器.我们可以看到ConvertUtilsBean的构造方法如下,它是在做什么捏?我们看人家老外的注释就知道,这是在创建一个拥有一个标准的转换器,并且将它注册.那个setFast是做什么的?这个是个模式(非设计模式,自己去看源代码的注释哈,暂时忽略)

    /** Construct a bean with standard converters registered */    public ConvertUtilsBean() {        converters.setFast(false);           deregister();        converters.setFast(true);    }
4.这个标准是什么,注册的时候发生了什么事情?注册很多标准的转换器.以下我只写出了 registerPrimitives(false);的方法中的转换器注册哈.另外的大多都是时间,URL,数组等等其他的标准转换器.如何标准法?就要去看每一个转换器里面是怎么个标准了哈.不过8个基本类型是有的,其中说明转换出异常的话,char是会变成" "而String是""哈.

    public void deregister() {        converters.clear();                registerPrimitives(false);        registerStandard(false, false);        registerOther(true);        registerArrays(false, 0);        register(BigDecimal.class, new BigDecimalConverter());        register(BigInteger.class, new BigIntegerConverter());    }
    private void registerPrimitives(boolean throwException) {        register(Boolean.TYPE,   throwException ? new BooleanConverter()    : new BooleanConverter(Boolean.FALSE));        register(Byte.TYPE,      throwException ? new ByteConverter()       : new ByteConverter(ZERO));        register(Character.TYPE, throwException ? new CharacterConverter()  : new CharacterConverter(SPACE));        register(Double.TYPE,    throwException ? new DoubleConverter()     : new DoubleConverter(ZERO));        register(Float.TYPE,     throwException ? new FloatConverter()      : new FloatConverter(ZERO));        register(Integer.TYPE,   throwException ? new IntegerConverter()    : new IntegerConverter(ZERO));        register(Long.TYPE,      throwException ? new LongConverter()       : new LongConverter(ZERO));        register(Short.TYPE,     throwException ? new ShortConverter()      : new ShortConverter(ZERO));    }

5.然后整个的BeanUtilsBean对象创建完毕后,执行真正的copyProperties已经开始.

    public void copyProperties(Object dest, Object orig)        throws IllegalAccessException, InvocationTargetException {        // Validate existence of the specified beans        if (dest == null) {            throw new IllegalArgumentException                    ("No destination bean specified");        }        if (orig == null) {            throw new IllegalArgumentException("No origin bean specified");        }        if (log.isDebugEnabled()) {            log.debug("BeanUtils.copyProperties(" + dest + ", " +                      orig + ")");        }        // Copy the properties, converting as necessary        if (orig instanceof DynaBean) {            DynaProperty[] origDescriptors =                ((DynaBean) orig).getDynaClass().getDynaProperties();            for (int i = 0; i < origDescriptors.length; i++) {                String name = origDescriptors[i].getName();                // Need to check isReadable() for WrapDynaBean                // (see Jira issue# BEANUTILS-61)                if (getPropertyUtils().isReadable(orig, name) &&                    getPropertyUtils().isWriteable(dest, name)) {                    Object value = ((DynaBean) orig).get(name);                    copyProperty(dest, name, value);                }            }        } else if (orig instanceof Map) {            Iterator entries = ((Map) orig).entrySet().iterator();            while (entries.hasNext()) {                Map.Entry entry = (Map.Entry) entries.next();                String name = (String)entry.getKey();                if (getPropertyUtils().isWriteable(dest, name)) {                    copyProperty(dest, name, entry.getValue());                }            }        } else /* if (orig is a standard JavaBean) */ {            PropertyDescriptor[] origDescriptors =                getPropertyUtils().getPropertyDescriptors(orig);            for (int i = 0; i < origDescriptors.length; i++) {                String name = origDescriptors[i].getName();                if ("class".equals(name)) {                    continue; // No point in trying to set an object's class                }                if (getPropertyUtils().isReadable(orig, name) &&                    getPropertyUtils().isWriteable(dest, name)) {                    try {                        Object value =                            getPropertyUtils().getSimpleProperty(orig, name);                        copyProperty(dest, name, value);                    } catch (NoSuchMethodException e) {                        // Should not happen                    }                }            }        }    }

6.好吧,由于这个BeanUtils用到了java的自省机制以及java的反射机制,所以我们可以看到,它只能对符合要求的进行copy.分别是DynaBean,Map,标准javabean.在符合三中规范的后,我们都会对其进行BeanUtilsBean中的public void copyProperty(Object bean, String name, Object value)方法.

public void copyProperty(Object bean, String name, Object value)        throws IllegalAccessException, InvocationTargetException {        // Trace logging (if enabled)        if (log.isTraceEnabled()) {            StringBuffer sb = new StringBuffer("  copyProperty(");            sb.append(bean);            sb.append(", ");            sb.append(name);            sb.append(", ");            if (value == null) {                sb.append("<NULL>");            } else if (value instanceof String) {                sb.append((String) value);            } else if (value instanceof String[]) {                String[] values = (String[]) value;                sb.append('[');                for (int i = 0; i < values.length; i++) {                    if (i > 0) {                        sb.append(',');                    }                    sb.append(values[i]);                }                sb.append(']');            } else {                sb.append(value.toString());            }            sb.append(')');            log.trace(sb.toString());        }        // Resolve any nested expression to get the actual target bean        Object target = bean;        Resolver resolver = getPropertyUtils().getResolver();        while (resolver.hasNested(name)) {            try {                target = getPropertyUtils().getProperty(target, resolver.next(name));                name = resolver.remove(name);            } catch (NoSuchMethodException e) {                return; // Skip this property setter            }        }        if (log.isTraceEnabled()) {            log.trace("    Target bean = " + target);            log.trace("    Target name = " + name);        }        // Declare local variables we will require        String propName = resolver.getProperty(name); // Simple name of target property        Class type = null;                            // Java type of target property        int index  = resolver.getIndex(name);         // Indexed subscript value (if any)        String key = resolver.getKey(name);           // Mapped key value (if any)        // Calculate the target property type        if (target instanceof DynaBean) {            DynaClass dynaClass = ((DynaBean) target).getDynaClass();            DynaProperty dynaProperty = dynaClass.getDynaProperty(propName);            if (dynaProperty == null) {                return; // Skip this property setter            }            type = dynaProperty.getType();        } else {            PropertyDescriptor descriptor = null;            try {                descriptor =                    getPropertyUtils().getPropertyDescriptor(target, name);                if (descriptor == null) {                    return; // Skip this property setter                }            } catch (NoSuchMethodException e) {                return; // Skip this property setter            }            type = descriptor.getPropertyType();            if (type == null) {                // Most likely an indexed setter on a POJB only                if (log.isTraceEnabled()) {                    log.trace("    target type for property '" +                              propName + "' is null, so skipping ths setter");                }                return;            }        }        if (log.isTraceEnabled()) {            log.trace("    target propName=" + propName + ", type=" +                      type + ", index=" + index + ", key=" + key);        }        // Convert the specified value to the required type and store it        if (index >= 0) {                    // Destination must be indexed            value = convert(value, type.getComponentType());            try {                getPropertyUtils().setIndexedProperty(target, propName,                                                 index, value);            } catch (NoSuchMethodException e) {                throw new InvocationTargetException                    (e, "Cannot set " + propName);            }        } else if (key != null) {            // Destination must be mapped            // Maps do not know what the preferred data type is,            // so perform no conversions at all            // FIXME - should we create or support a TypedMap?            try {                getPropertyUtils().setMappedProperty(target, propName,                                                key, value);            } catch (NoSuchMethodException e) {                throw new InvocationTargetException                    (e, "Cannot set " + propName);            }        } else {                             // Destination must be simple            value = convert(value, type);            try {                getPropertyUtils().setSimpleProperty(target, propName, value);            } catch (NoSuchMethodException e) {                throw new InvocationTargetException                    (e, "Cannot set " + propName);            }        }    }
7.我们可以看到其中对值进行了类型转换.如果我们转换的时候出了错,就会直接抛出异常.所以我们自己用的时候有些类为了避免抛出异常而不直接赋予默认值,例如我们在copy一个我们自己创建的类的时候,如果转换当中有问题存在,那么就可能直接抛出异常,这个时候我们就要像他那样注册转换器,以至于出问题了是采取一些默认的处理,而不是直接抛出异常.我们的转换器必须实现以下接口,
public interface Converter
并且使用下面的静态方法进行注册.

    public static void register(Converter converter, Class clazz) {        ConvertUtilsBean.getInstance().register(converter, clazz);    }
需要说明的一点是,转换器有个标准库,但是我们可以对立面的任何一个进行覆盖.程序只会认我们最后注册的转换器(如果该转换器之前注册过).比如我们看上上上面的代码知道一个Long的值如果为空,则会默认赋值ZERO,如果写一条语句进行覆盖.

ConvertUtils.register(new LongConverter(null), Long.class);
那么如果转换Long类型的值出了异常,则会返回默认值 null,而不是之前的ZERO.


第一次写博客,加入csdn的大家庭,忘各位前辈有任何问题直接指点- -.接受各种教诲!~~~