Apache BeanUtils 拷贝属性的实现
先说坑:
- 对于装箱的Boolean类型,不能用isABC和setABC的组合,只能用getABC与setABC的组合(对于任何非基本类型,都是这样)
- beanUtils的BeanUtils.copyProperties看的不是成员,而是get与set方法,也就是属性(property),对于基本boolean是看is与set
本文环境:
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
commons-beanutils 1.9.2
实现:
通过class获取BeanInfo(beanInfo = Introspector.getBeanInfo(icontext.getTargetClass());
具体代码在 org.apache.commons.beanutils.DefaultBeanIntrospector.introspect(IntrospectionContext icontext) line 88,
BeanInfo中描述了很多属性如:
对于方法的描述在methods中,对于属性(Property)的描述在PropertiesDescripter中。
最终copy的时候是通过反射调用readMethodName和writeMethodName来进行的复制,在复制之前还会进行类型的检查。
注意:
对于属性的获取是通过java的Introspector来获取的并不是直接反射的私有成员变量。Introspector这个类Eclipse在debug的时候无法追踪临时变量,原因未知…
如果对于装箱的Boolean类型使用is与set,会出现如下情况:
readMethodName是一个null…
看下Introspector类往下走,获取类的Properties的源码如下(在Introspector.getTargetPropertyInfo方法里,line : 507):
try { if (argCount == 0) { if (name.startsWith(GET_PREFIX)) { // Simple getter pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null); } else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) { //上面的这个是小写的boolean.class 并不是Boolean.class,指的是基本类型的那个boolean // Boolean getter pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null); } } else if (argCount == 1) { if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) { pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null); } else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) { // Simple setter pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method); if (throwsException(method, PropertyVetoException.class)) { pd.setConstrained(true); } } } else if (argCount == 2) { if (void.class.equals(resultType) && int.class.equals(argTypes[0]) && name.startsWith(SET_PREFIX)) { pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, null, method); if (throwsException(method, PropertyVetoException.class)) { pd.setConstrained(true); } } } } catch (IntrospectionException ex) { // This happens if a PropertyDescriptor or IndexedPropertyDescriptor // constructor fins that the method violates details of the deisgn // pattern, e.g. by having an empty name, or a getter returning // void , or whatever. pd = null; } }
原理还是根据set和get/is和set去割字符串然后拼接property。
java对于java bean的规范可以在这里下载:
其中关于Property有如下说明:
7 Properties
Properties are discrete, named attributes of a Java Bean that can affect its appearance or its behaviour.
…
Properties show up in a number of ways:
1. Properties may be exposed in scripting environments as though they were fields of
objects. So in a Javascript environment I might do “b.Label = foo” to set the value of a
property.
2. Properties can be accessed programmatically by other components calling their getter
and setter methods (see Section 7.1 below).
3. As part of the process of customizing a component (see Section 9), its properties may
be presented in a property sheet for a user to edit.
4. Typically a bean’s properties will be persistent, so that their state will be stored away
as part of the persistent state of the bean.
Properties can have arbitrary types, including both built-in Java types such as “int” and class
or interfaces types such as “java.awt.Color”.
…
7.1 Accessor methods
Properties are always accessed via method calls on their owning object. For readable properties
there will be a getter method to read the property value. For writable properties there will be a
setter method to allow the property value to be updated. Thus even when a script writer types
in something such as “b.Label = foo” there is still a method call into the target object to set the
property, and the target object has full programmatic control.
So properties need not just be simple data fields, they can actually be computed values. Updates
may have various programmatic side effects. For example, changing a bean’s background color
property might also cause the bean to be repainted with the new color.
For simple properties the accessor type signatures are:
void setFoo(PropertyType value); // simple setter
PropertyType getFoo(); // simple getter
GetFoo and setFoo are simply example names. Accessor methods can have arbitrary names.
However for standard naming conventions for accessor methods see the design patterns described
in Section 8.3.
…
8.3 Design Patterns for Properties
8.3.1 Simple properties
By default, we use design patterns to locate properties by looking for methods of the form:
public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> a);
If we discover a matching pair of “get<PropertyName>” and “set<PropertyName>” methods
that take and return the same type, then we regard these methods as defining a read-write property
whose name will be “<propertyName>”. We will use the “get<PropertyName>” method
to get the property value and the “set<PropertyName>” method to set the property value. The
pair of methods may be located either in the same class or one may be in a base class and the
other may be in a derived class.
If we find only one of these methods, then we regard it as defining either a read-only or a writeonly
property called “<propertyName>”
By default we assume that properties are neither bound nor constrained (see Section 7).
So a simple read-write property “foo” might be represented by a pair of methods:
public Wombat getFoo();
public void setFoo(Wombat w);
8.3.2 Boolean properties
In addition, for boolean properties, we allow a getter method to match the pattern:
public boolean is<PropertyName>();
This “is<PropertyName>” method may be provided instead of a “get<PropertyName>” method,
or it may be provided in addition to a “get<PropertyName>” method.
In either case, if the “is<PropertyName>” method is present for a boolean property then we will
use the “is<PropertyName>” method to read the property value.
An example boolean property might be:
public boolean isMarsupial();
public void setMarsupial(boolean m);