在咱们着手一个Java Web项目的时候,常常会遇到DO、VO、DTO
对象之间的属性拷贝,若采用get、set
的方法来进行赋值的话,代码会至关冗长丑陋,通常咱们会采用Spring
的BeanUtils
类来进行属性拷贝,其基本原理就是经过Java的反射机制,下面咱们来看一下源码的具体实现。java
在分析源码前,咱们先温习一下如下的知识点。segmentfault
在Java中万物皆对象,并且咱们在代码中写的每个类也都是对象,是java.lang.Class
类的对象。因此,每一个类都有本身的实例对象,并且它们本身也都是Class
类的对象。api
咱们来看一下Class
类的构造方法:缓存
private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
Class类的构造方法是私有的,只有JVM能够建立该类的对象,所以咱们没法在代码中经过new
的方式显示声明一个Class对象。安全
可是,咱们依然有其余方式得到Class类的对象:app
1.经过类的静态成员变量ide
Class clazz = Test.class;
2.经过对象的getClass()方法工具
Class clazz = test.getClass();
3.经过Class的静态方法forName()源码分析
// forName须要传入类的全路径 Class clazz = Class.forName("destiny.iron.api.model.Test");
基本类型和其对应的包装类的Class对象是不相等的,即long.class != Long.class
。性能
PropertyDescriptor
类表示的是标准形式的Java Bean经过存取器(即get set方法)导出的一个属性,好比,咱们能够经过如下方式,对对象的属性进行赋值:
public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } public static void main(String[] args) throws Exception { Person test1 = new Person(); test1.setName("vvvv"); PropertyDescriptor pd = new PropertyDescriptor("name", test1.getClass()); Method setMethod = pd.getWriteMethod(); // 还有与Wirte方法对应的Read方法 setMethod.invoke(test1, "bbbbb"); System.out.print(test1); } }
Java中有strong、soft、weak、phantom
四种引用类型,下面介绍一下soft引用和weak引用:
Soft Reference
: 当对象是Soft reference
可达时,向系统申请更多内存,GC不是直接回收它,而是当内存不足的时候才回收它。所以Soft reference适合用于构建一些缓存系统。Weak Reference
: 弱引用的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次GC发生以前。当垃圾收集器工做时,不管当前内存是否足够,都会回收掉只被弱引用关联的对象。private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException { // 检查source和target对象是否为null,不然抛运行时异常 Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); // 获取target对象的类信息 Class<?> actualEditable = target.getClass(); // 若editable不为null,检查target对象是不是editable类的实例,若不是则抛出运行时异常 // 这里的editable类是为了作属性拷贝时限制用的 // 若actualEditable和editable相同,则拷贝actualEditable的全部属性 // 若actualEditable是editable的子类,则只拷贝editable类中的属性 if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]"); } actualEditable = editable; } // 获取目标类的全部PropertyDescriptor,getPropertyDescriptors这个方法请看下方 PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null); for (PropertyDescriptor targetPd : targetPds) { // 获取该属性对应的set方法 Method writeMethod = targetPd.getWriteMethod(); // 属性的set方法存在 且 该属性不包含在忽略属性列表中 if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { // 获取source类相同名字的PropertyDescriptor, getPropertyDescriptor的具体实现看下方 PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { // 获取对应的get方法 Method readMethod = sourcePd.getReadMethod(); // set方法存在 且 target的set方法的入参是source的get方法返回值的父类或父接口或者类型相同 // 具体ClassUtils.isAssignable()的实现方式请看下面详解 if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { try { //get方法是不是public的 if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { //暴力反射,取消权限控制检查 readMethod.setAccessible(true); } //获取get方法的返回值 Object value = readMethod.invoke(source); // 原理同上 if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } // 将get方法的返回值 赋值给set方法做为入参 writeMethod.invoke(target, value); } catch (Throwable ex) { throw new FatalBeanException( "Could not copy property '" + targetPd.getName() + "' from source to target", ex); } } } } } }
getPropertyDescriptors
源码:
public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException { // CachedIntrospectionResults类是对PropertyDescriptor的一个封装实现,看forClass方法的实现 CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); return cr.getPropertyDescriptors(); } @SuppressWarnings("unchecked") static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException { // strongClassCache的声明以下: // strongClassCache = new ConcurrentHashMap<Class<?>, CachedIntrospectionResults>(64); // 即将Class做为key,CachedIntrospectionResults做为value的map, // 因为线程安全的须要,使用ConcurrentHashMap做为实现 CachedIntrospectionResults results = strongClassCache.get(beanClass); if (results != null) { return results; } // 若strongClassCache中不存在,则去softClassCache去获取,softClassCache的声明以下 // softClassCache = new ConcurrentReferenceHashMap<Class<?>, CachedIntrospectionResults>(64); // ConcurrentReferenceHashMap是Spring实现的能够指定entry引用级别的ConcurrentHashMap,默认的引用级别是soft,能够防止OOM results = softClassCache.get(beanClass); if (results != null) { return results; } results = new CachedIntrospectionResults(beanClass); ConcurrentMap<Class<?>, CachedIntrospectionResults> classCacheToUse; // isCacheSafe方法检查给定的beanClass是否由入参中的classloader或者此classloader的祖先加载的(双亲委派的原理) // isClassLoaderAccepted检查加载beanClass的classloader是否在能够接受的classloader的集合中 或者是集合中classloader的祖先 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; } // 根据classloader的结果,将类信息加载到对应的缓存中 CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results); return (existing != null ? existing : results); }
isAssignable
源码:
public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) { Assert.notNull(lhsType, "Left-hand side type must not be null"); Assert.notNull(rhsType, "Right-hand side type must not be null"); // 若左边类型 是右边类型的父类、父接口,或者左边类型等于右边类型 if (lhsType.isAssignableFrom(rhsType)) { return true; } // 左边入参是不是基本类型 if (lhsType.isPrimitive()) { //primitiveWrapperTypeMap是从包装类型到基本类型的map,将右边入参转化为基本类型 Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType); if (lhsType == resolvedPrimitive) { return true; } } else { // 将右边入参转化为包装类型 Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType); if (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper)) { return true; } } return false; }
ClassUtils.isAssignable()
方法扩展了Class的isAssignableFrom()
方法,即将Java
的基本类型和包装类型作了兼容。
一个看似简单的BeanUtils
工具类,其实里面包含的Java基础的知识点很是多,包括类型信息、反射、线程安全、引用类型、类加载器等。Spring
的BeanUtils
的实现里使用了ConcurrentHashMap
做为缓存,每次去获取PropertyDescriptor
时,能够直接去缓存里面获取,而没必要每次都去调用native
方法,因此Spring
的BeanUtils
的性能仍是很不错的。