1. A common way copy object properties and performances of
In everyday coding, often encounter convert between DO, DTO object if the attribute of the object itself is relatively small when we have hard-coded manual setter also ok, but if the properties of an object under more circumstances, it is hand-setter and low and inefficient. This time we consider a number of tools to copy the object attributes.
Our common way to copy object properties are:
Hard Code
net.sf.cglib.beans.BeanCopier#copy
org.springframework.beans.BeanUtils.copyProperties
org.apache.commons.beanutils.PropertyUtils.copyProperties
org.apache.commons.beanutils.BeanUtils.copyProperties
For more than a copy of the way, I made a simple performance testing results are as follows:
Copy format | The number of objects: 1 | The number of objects: 1000 | The number of objects: 100000 | The number of objects: 1000000 |
---|---|---|---|---|
Hard Code |
0 ms | 1 ms | 18 ms | 43 ms |
cglib.BeanCopier |
111 ms | 117 ms | 107 ms | 110 ms |
spring.BeanUtils |
116 ms | 137 ms | 246 ms | 895 ms |
apache.PropertyUtils |
167 ms | 212 ms | 601 ms | 7869 ms |
apache.BeanUtils |
167 ms | 275 ms | 1732 ms | 12380 ms |
Test environment: OS =
macOS 10.14
, = the CPU2.5 GHz,Intel Core I7
, Memory =16 GB, 2133MHz LPDDR3
Test Method: complex objects by a specified number to a copy, performed separately for each Case
10
times the mean value
Version:commons-beanutils:commons-beanutils:1.9.3
,org.springframework:spring-beans:4.3.5.RELEASE
,cglib:cglib:2.2.2
Conclusion : From the test results it is clear that use Hard Code
manner Copy object attributes best performance; with net.sf.cglib.beans.BeanCopier#copy
the way most stable copy object properties; and org.apache.commons.beanutils.BeanUtils.copyProperties
the way the data amount when the most powerful performance degradation. So when they encounter an object has more attributes in daily programming attribute copy priority uses net.sf.cglib.beans.BeanCopier#copy
.
The reason of the above data generated huge gap in their different ways of realization of the principle and caused, Hard Code directly call the getter & setter
method value, cglib
uses 字节码技术
, and then three kinds are used in 反射
the way. Differences generated when the excellent performance of the first two we all know, but why be reflected by way of the same attributes Copy it so great? This is what this paper we want to explore the content.
We first reading
org.apache.commons.beanutils.BeanUtils
the source code, followed by readingorg.springframework.beans.BeanUtils
the source code, and finally to demonstrate the performance difference through their respective implementation
apache.BeanUtils
And spring.BeanUtils
are reflective technology, also called Java Advanced API-- on reflection Introspector
(introspection), so we must first understand Introspector
what it is.
2. Introspector
Introspector(内省)
Jdk is provided for describing the Java bean
properties of the support, tool and method of the event; can be obtained by using such BeanInfo
an object that implements the interface, BeanInfo
the interface has two important ways:
BeanDescriptor getBeanDescriptor();
,BeanDescriptor
It provides some java bean global information, such as the class type, class name, etc.PropertyDescriptor[] getPropertyDescriptors()
PropertyDescriptor
It describes the java bean in a property and derive theirgetter & setter
methodsSoftReference
Jdk introspection interfaces greatly simplifies the way the reflection of such information, we can easily call the java bean reflected by this group api. This group of soft api reference phantom reference to take advantage of the free memory; in certain places (e.g. declaredMethodCache
) using caching api efficiency to speed, and this group is a thread-safe api.
Use:
BeanInfo beanInfo = Introspector.getBeanInfo(icontext.getTargetClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor descriptor: descriptors) {
Method readMethod = descriptor.getReadMethod();
Method writeMethod = descriptot.getWriteMethod();
// readMethod.invoke(...);
}
Above is about the Introspector
simple understanding, let's first look at apache.BeanUtils
the source code.
3. Source: apache.BeanUtils
apache.BeanUtils
It is a static method contains a lot of tools, and almost all are static methods BeanUtilsBean
singleton object implementations provide. BeanUtilsBean
Inlet JavaBean properties is a method of operation, it provides a single example of the external function. But there is a place different from a normal singleton: different class loaders have a different example, only one instance of each class loader , so there is actually a single embodiment of a pseudo-single embodiment singletion pseudo .
// ContextClassLoaderLocal对象管理了BeanUtilsBean的所有实例
private static final ContextClassLoaderLocal<BeanUtilsBean>
BEANS_BY_CLASSLOADER = new ContextClassLoaderLocal<BeanUtilsBean>() {
@Override
protected BeanUtilsBean initialValue() {
return new BeanUtilsBean();
}
};
public static BeanUtilsBean getInstance() {
return BEANS_BY_CLASSLOADER.get();
}
// {@link ContextClassLoaderLocal#get}
public synchronized T get() {
valueByClassLoader.isEmpty();
try {
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); // 获取当前线程的类加载器
if (contextClassLoader != null) {
T value = valueByClassLoader.get(contextClassLoader);
if ((value == null)
&& !valueByClassLoader.containsKey(contextClassLoader)) {
value = initialValue(); // 初始化BeanUtilsBean,即 new BeanUtilsBean();
valueByClassLoader.put(contextClassLoader, value);
}
return value;
}
} catch (final SecurityException e) { /* SWALLOW - should we log this? */ }
if (!globalValueInitialized) {
globalValue = initialValue();
globalValueInitialized = true;
}
return globalValue;
}
When to get BeanUtilsBean
after instance, the next step is when we copy the object properties.
// omit exception
public static void copyProperties(final Object dest, final Object orig){
BeanUtilsBean.getInstance().copyProperties(dest, orig);
}
In the copyProperties
method, the object for the type of original employ different logic:
Map
: Map of matching by the dest Key attributes, then the assignment;DynaBean
:DynaBean
The name implies, it is a dynamic java bean objects may be formed, i.e. it stores the internal name of the attribute type and the corresponding value is the attribute name and the attribute name inside the object corresponding to dest when copy attribute assignment;标准Java Bean
: This is the type of our main analysis, it is standard JavaBean objects; the only difference between the two lies in the handle before the value of the original bean.
Steps of 3.1 attribute copy for the standard JavaBean
public void copyProperties(final Object dest, final Object orig) {
// omit some code (省略一部分代码) ...
final PropertyDescriptor[] origDescriptors = getPropertyUtils().getPropertyDescriptors(orig);
for (PropertyDescriptor origDescriptor : origDescriptors) {
final String name = origDescriptor.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 {
final Object value =
getPropertyUtils().getSimpleProperty(orig, name);
copyProperty(dest, name, value);
} catch (final NoSuchMethodException e) {
// Should not happen
}
}
}
}
- Depending on the type of parsing the original bean, which cache
PropertyDescriptor
- Each of the original bean polling
PropertyDescriptor
to determinePropertyDescriptor
whether readable in the original bean, whether written in the target bean, only when two conditions are established with copy qualifications - The
PropertyDescriptor
acquired corresponding values from the original bean, the property value corresponds to the copy target bean
Gets the PropertyDescriptor Bean 3.2
final PropertyDescriptor[] origDescriptors =
getPropertyUtils().getPropertyDescriptors(orig);
Gets PropertyDescriptor
entrusted to PropertyUtilsBean
object to implement:
public BeanUtilsBean() {
this(new ConvertUtilsBean(), new PropertyUtilsBean());
}
PropertyUtilsBean
Is an API to operate using java reflection on Java Bean getter and setter methods, such code originally located BeanUtilsBean
in, but considering the reason code amount were isolated ( Much of this code was originally included in BeanUtils, but has been separated because of the volume of code involved
).
In PropertyUtilsBean
each of the Bean PropertyDescriptor
will be stored BeanIntrospectionData
, every time the need to get the object PropertyDescriptor
, the acquisition will start in cahche BeanIntrospectionData
; if not, the API obtained by introspection BeanIntrospectionData
and placed in the cache:
private BeanIntrospectionData getIntrospectionData(final Class<?> beanClass) {
// omit some check code ...
BeanIntrospectionData data = descriptorsCache.get(beanClass);
if (data == null) {
data = fetchIntrospectionData(beanClass);
descriptorsCache.put(beanClass, data);
}
return data;
}
private BeanIntrospectionData fetchIntrospectionData(final Class<?> beanClass) {
final DefaultIntrospectionContext ictx = new DefaultIntrospectionContext(beanClass);
for (final BeanIntrospector bi : introspectors) {
try {
bi.introspect(ictx);
} catch (final IntrospectionException iex) {
log.error("Exception during introspection", iex);
}
}
return new BeanIntrospectionData(ictx.getPropertyDescriptors());
}
In the fetchIntrospectionData()
process, the built-in device introspection DefaultBeanIntrospector
information using introspection java API to transfer acquired DefaultIntrospectionContext
, through DefaultIntrospectionContext
configuration BeanIntrospectionData
. DefaultBeanIntrospector
Specific code:
public void introspect(final IntrospectionContext icontext) {
BeanInfo beanInfo = null;
try {
// JAVA 的 Instrospector
beanInfo = Introspector.getBeanInfo(icontext.getTargetClass());
} catch (final IntrospectionException e) {
return;
}
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
if (descriptors == null) {
descriptors = new PropertyDescriptor[0];
}
// 解决IndexedPropertyDescriptor在不同版本的JDK下的差异
handleIndexedPropertyDescriptors(icontext.getTargetClass(), descriptors);
icontext.addPropertyDescriptors(descriptors);
}
3.3 is determined whether the attribute readable / writable
To attribute copy, it must first ensure that the properties of the original object is readable, writable target object property. In PropertyUtilsBean
through the isWriteable(); isReadable()
method, the two methods seem long, we deal with on exception is omitted and out of view under:
public boolean isReadable(Object bean, String name) {
// Omit Validate method parameters
// Resolve nested references, 解析内嵌的属性,形如 student.name
while (resolver.hasNested(name)) {
final String next = resolver.next(name);
Object nestedBean = nestedBean = getProperty(bean, next);
if (nestedBean == null) {
throw new NestedNullException("Null property value for);
}
bean = nestedBean;
name = resolver.remove(name);
}
// Remove any subscript from the final name value, 在最终的方法名中移除所有的下标
name = resolver.getProperty(name);
if (bean instanceof WrapDynaBean) {
bean = ((WrapDynaBean)bean).getInstance();
}
if (bean instanceof DynaBean) {
// All DynaBean properties are readable,所有DynaBean的属性均是可读的
return (((DynaBean) bean).getDynaClass().getDynaProperty(name) != null);
} else {
final PropertyDescriptor desc = getPropertyDescriptor(bean, name);
if (desc != null) {
Method readMethod = getReadMethod(bean.getClass(), desc);
if (readMethod == null) {
if (desc instanceof IndexedPropertyDescriptor) {
readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod();
} else if (desc instanceof MappedPropertyDescriptor) {
readMethod = ((MappedPropertyDescriptor) desc).getMappedReadMethod();
}
readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
}
return (readMethod != null);
} else {
return (false);
}
}
}
From the above we can see that the code, read each attribute may need to write acquired Method each use, and then determination, and also requires the processing DynaBean, Nested logic; when we Copy bulk properties, still need to perform the above steps, the result of the determination method will not be cached, and this is the reason that compared to the other jar inefficient.
3.4 Bean read the property value of the original, set the property value of the target Bean's
We omit the code to determine the validity and unusual of them:
public Object getSimpleProperty(final Object bean, final String name)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
// omit check null code ...
// 校验属性
if (resolver.hasNested(name)) {
throw new IllegalArgumentException
("Nested property names are not allowed: Property '" +
name + "' on bean class '" + bean.getClass() + "'");
} else if (resolver.isIndexed(name)) {
throw new IllegalArgumentException
("Indexed property names are not allowed: Property '" +
name + "' on bean class '" + bean.getClass() + "'");
} else if (resolver.isMapped(name)) {
throw new IllegalArgumentException
("Mapped property names are not allowed: Property '" +
name + "' on bean class '" + bean.getClass() + "'");
}
// DynaBean的特殊逻辑
if (bean instanceof DynaBean) {
final DynaProperty descriptor =
((DynaBean) bean).getDynaClass().getDynaProperty(name);
if (descriptor == null) {
throw new NoSuchMethodException("Unknown property '" +
name + "' on dynaclass '" +
((DynaBean) bean).getDynaClass() + "'" );
}
return (((DynaBean) bean).get(name));
}
final PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
if (descriptor == null) {
throw new NoSuchMethodException("Unknown property '" +
name + "' on class '" + bean.getClass() + "'" );
}
// 获取getter方法
final Method readMethod = getReadMethod(bean.getClass(), descriptor);
if (readMethod == null) {
throw new NoSuchMethodException("Property '" + name +
"' has no getter method in class '" + bean.getClass() + "'");
}
// 调用getter方法读取值
final Object value = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
return (value);
}
The above is a method to read the property value. After reading the property value is set to the target value of the bean. In BeanUtilsBean
implementation, the repeated processing logic and embedded logic DynaBean property, ultimately obtained which impart the target value setter method Bean.
4. Source: spring.BeanUtils
BeanUtils
Located spring-beans
module exposes static methods copyProperties
for performing attribute copy, each copyProperties
eventually have to call a private static method implemented properties copy:
private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties){
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
// 第一步 调用Java 内省API 获取PropertyDescriptor
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
// 第二步 轮询目标bean的PropertyDescriptor
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
// 判断是否存在setter方法以及属性是否在需要忽略的属性列表中
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
// 获取源bean的PropertyDescriptor
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
// 获取getter方法
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
// 如果getter方法不是public,则需要设置其accessible
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
// 反射获取属性值
Object value = readMethod.invoke(source);
// 如果setter方法不是public则需要设置其accessible
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
// 反射赋值
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
Gets the PropertyDescriptor Bean 4.1
spring.BeanUtils
For the bean in the PropertyDescriptor
process and the cache are made CachedIntrospectionResults
for processing class. CacheIntrospectionResults
The static data cached in the collection, a method using a design pattern of the plant, by forClass(Class)
methods expose cache:
static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException {
// 从缓存中获取CachedIntrospectionResults
CachedIntrospectionResults results = strongClassCache.get(beanClass);
if (results != null) {
return results;
}
// 从缓存中获取CachedIntrospectionResults
results = softClassCache.get(beanClass);
if (results != null) {
return results;
}
// 构造CachedIntrospectionResults
results = new CachedIntrospectionResults(beanClass);
ConcurrentMap<Class<?>, CachedIntrospectionResults> classCacheToUse;
// 选取对应的缓存
if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
isClassLoaderAccepted(beanClass.getClassLoader())) {
classCacheToUse = strongClassCache;
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
}
classCacheToUse = softClassCache;
}
CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results);
return (existing != null ? existing : results);
}
We can see here have two cache: strongClassCache
and softClassCache
that They what difference does it?
First we look at their definitions:
static final ConcurrentMap<Class<?>, CachedIntrospectionResults> strongClassCache =
new ConcurrentHashMap<Class<?>, CachedIntrospectionResults>(64);
static final ConcurrentMap<Class<?>, CachedIntrospectionResults> softClassCache =
new ConcurrentReferenceHashMap<Class<?>, CachedIntrospectionResults>(64);
ConcurrentReferenceHashMap can specify the reference level corresponding to that segment locks for internal use, with the principles of jdk1.7 ConcurrentMap similar implementation.
strongClassCache
Is held in the cache 强引用
, and the softClassCache
holding cache is 软引用
( the JDK has 4 levels of reference, references are strong, soft, weak, and reference phantom reference, reference examples reflected when determining the level held by GC recovered timing ).
strongClassCache
For caching cache-safe
the bean class data, and softClassCache
for caching none-cache-safe
bean class data; strongClassCache
consistent with data in the spring file application life cycle, softClassCache
life cycle managed not by the spring, so in order to prevent lead classloader prematurely closed 内存泄漏
, where soft reference cache.
What kind of data is in the cache strongClassCache
in it? beanClass ClassLoader is the same as the current program or the specified ClassLoader will be stored in the same strongClassCache
, the rest are stored softClassCache
in.
If the data does not get more from the cache, so will new CachedIntrospectionResults(Class)
the corresponding call the relevant API Java Introspector ARE constructor:
// omit some logger code ...
private CachedIntrospectionResults(Class<?> beanClass) throws BeansException {
try {
BeanInfo beanInfo = null;
// 对一些特殊的set方法(setA(int index, Object a))或者list的set方法进行处理
for (BeanInfoFactory beanInfoFactory : beanInfoFactories) {
beanInfo = beanInfoFactory.getBeanInfo(beanClass);
if (beanInfo != null) {
break;
}
}
if (beanInfo == null) {
// fall back到默认获取BeanInfo的方式
beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ?
Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) : Introspector.getBeanInfo(beanClass));
}
this.beanInfo = beanInfo;
// propertyDescriptor缓存
this.propertyDescriptorCache = new LinkedHashMap<String, PropertyDescriptor>();
// 考虑到性能原因,对于每个PropertyDescriptor只处理一次
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (Class.class == beanClass &&
("classLoader".equals(pd.getName()) || "protectionDomain".equals(pd.getName()))) {
continue;
}
// 重新包装为GenericTypeAwarePropertyDescriptor
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
this.propertyDescriptorCache.put(pd.getName(), pd);
}
// 检查Java 8在接口中的默认实现方法
Class<?> clazz = beanClass;
while (clazz != null) {
Class<?>[] ifcs = clazz.getInterfaces();
for (Class<?> ifc : ifcs) {
BeanInfo ifcInfo = Introspector.getBeanInfo(ifc, Introspector.IGNORE_ALL_BEANINFO);
PropertyDescriptor[] ifcPds = ifcInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : ifcPds) {
if (!this.propertyDescriptorCache.containsKey(pd.getName())) {
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
this.propertyDescriptorCache.put(pd.getName(), pd);
}
}
}
clazz = clazz.getSuperclass();
}
this.typeDescriptorCache = new ConcurrentReferenceHashMap<PropertyDescriptor, TypeDescriptor>();
}
catch (IntrospectionException ex) {
throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex);
}
}
The main role of this code is to get through introspection BeanInfo interface and then PropertyDescriptor
cached. specific process:
- First get BeanInfo by BeanInfoFactory; here by default when registering
BeanInfoFactory
ShiExtendedBeanInfoFactory
, such a major deal includes some specialset
methods of bean:
public static boolean isCandidateWriteMethod(Method method) {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
int nParams = parameterTypes.length;
return (methodName.length() > 3 && methodName.startsWith("set") && Modifier.isPublic(method.getModifiers()) &&
(!void.class.isAssignableFrom(method.getReturnType()) || Modifier.isStatic(method.getModifiers())) &&
(nParams == 1 || (nParams == 2 && int.class == parameterTypes[0])));
}
If a bean is included in such a method: to set
begin with && (return value is not void
|| static method) && (|| having a parameter which has two parameters is the first parameter int
), the form:
// void.class.isAssignableFrom(method.getReturnType()) 方法返回值不为void
public Bean setFoo(Foo foo) {
this.foo = foo;
return this;
}
public static void setFoos(Foo foo) {
Bean.foo = foo;
}
public Bean setFoos(int index, Foo foo) {
this.foos.set(index, foo);
return this;
}
If the bean does not contain the above-described method, using the Java introspection directly acquired API
BeanInfo
When to get
BeanInfo
after it canPropertyDescriptor
be cached; this will bePropertyDescriptor
repackaged asGenericTypeAwarePropertyDescriptor
, Reasons for this package is to reprocess BridgeMethod , popular speak, is to deal with the current class inherits the generic class or implement generic interface, then how recognition of these methods?Bridge Method
: Bridging method is the introduction of a generic jdk jdk for compatibility with previous versions, automatically generated at compile time method. Flag bridging method bytecode will be labeled asACC_BRIDGE
(bridge method) andACC_SYNTHETIC
(generated by the compiler). ThroughMethod.isBridge()
to a method for determining whether aBridgeMethod
. If a method overrides a generic parent or a generic interface implemented is generatedbridge method
.
public static Method findBridgedMethod(Method bridgeMethod) {
if (bridgeMethod == null || !bridgeMethod.isBridge()) {
return bridgeMethod;
}
// 获取所有与bridgeMethod名称、参数数量相匹配的方法(包括父类)
List<Method> candidateMethods = new ArrayList<Method>();
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
for (Method candidateMethod : methods) {
// candidateMethod是`Bridge Method`时将其加入候选方法列表
if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
candidateMethods.add(candidateMethod);
}
}
if (candidateMethods.size() == 1) {
return candidateMethods.get(0);
}
// 在众候选方法中找到其BridgeMethod,如果找不到返回原方法
Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod);
if (bridgedMethod != null) {
return bridgedMethod;
}else {
return bridgeMethod;
}
}
- Method processed class, interface implemented method must deal with. In Java8, the interface can have a default method, for example:
public interface MethodAvailable {
default String getHello(){
return "hello";
}
String setHello(String hello);
}
Consistent with processing logic and processing logic interface class for the method implemented in the implementation of the method.
After completion of the above procedure, we got the result that buffers introspection CachedIntrospectionResults
example, and select the corresponding cahche, the results cached together. (Cahce selection process previously described process of reading cache consistency);
4.2 copy property values
Acquired from the cache to the target class PropertyDescriptor
, the polling each of which will PropertyDescriptor
assign a.
The assignment process is relatively simple point:
- Write method of acquiring the target class (
setter
) - If the write method in the target is not empty and the method in the corresponding attribute is not configured
igonreList
(ignoring attribute list), the method reads the corresponding attribute is acquired source class (getter
) - After acquiring the reading method, reading method needs to determine whether the return parameter values and write method is the same type, different types of course can not copy the
- Reading method to determine whether
public
, and if not, you need to set access permissionsmethod.setAccessible(true);
(non-public method to access when you need to set the reflectionsetAccessible(true)
to gain access), and then call the reflective perform this methodinvoke(source)
; - Write method to determine whether
public
, if it is not set access permissions, and then read the value assigned by the radiation target classinvoke(taget, value)
;
At this point, the class attribute copy is complete.
5. Summary
For performance reasons and apache.BeanUtils the difference between 5.1 spring.BeanUtils
When a large number of copy, apache.BeanUtils
compared to spring.BeanUtils
slow in recent 14
times, the reason, in fact, is the following:
apache.BeanUtils
In each class loader implements a cacheBeanUtilsBean
instance, on getting this instance will lock (synchronized
)apache.BeanUtils
SupportDynaBean
andMap
ability to Object mapping, but for the post-PropertyDescriptor
processing, even though I used a simple Object, also to judgeDynaBean
andMap
, where if the policy mode to separate it should reduce a lot of time to judgeapache.BeanUtils
Each time the property copy execution, from re-PropertyDescriptor
acquiring reading and writing method, althoughPropertyDescriptor
is cached, but every acquisitionreadMethod/writeMethod
is also very time-consuming, especially when a larger number of object instances, if here forreadMethod/writeMethod
cache, performance should be many upgrade- In contrast
spring.BeanUtils
reason thanapache.BeanUtils
fast, is itsPropertyDescriptor
first cache only after treatment. In contrast visible toPropertyDescriptor
the process it is very time-consuming.
5.2 Harvest
Through this inquiry, we learned the following knowledge:
Java Introspectpr
In time of reflection before use, and are based on primitive methods to obtain information and cache then the Map; such defects is when the different modules require reflection, if the result of poor communication leads to another man by when the raw reflected interface to obtain class information, the cache can not be utilized; then using introspection, JDK default cache.Bridge Method
Before generics erased understanding only stay at compile generics will be erased, understandbridge method
, the mechanism for generics has also been more understanding- When the properties of each copy mode usage scenarios:
- For higher performance requirements when the recommended method is called manual
- General recommended scenario
net.sf.cglib.beans.BeanCopier#copy
- If you take into account the risk of introducing new jar package recommended
org.springframework.beans.BeanUtils.copyProperties