Spring BeanUtils 소스 코드 분석

배경
우리 가 자바 웹 프로젝트 에 착수 할 때DO、VO、DTO대상 간 의 속성 복사 본 을 자주 만 날 수 있다.만약 에get、set의 방법 으로 값 을 부여 하면 코드 가 상당히 지루 하고 추 할 것 이다.보통 우 리 는SpringBeanUtils유형 으로 속성 복사 하 는데 그 기본 원 리 는 자바 의 반사 체 제 를 통 해다음은 소스 코드 의 구체 적 인 실현 을 살 펴 보 겠 습 니 다.
선행 지식
소스 코드 를 분석 하기 전에 우 리 는 먼저 아래 의 지식 점 을 복습 합 시다.
java.lang.Class 클래스
자바 에서 만물 은 모두 대상 이 고 우리 가 코드 에 쓴 모든 유형 도 대상 이 며java.lang.Class류 의 대상 이다.그래서 모든 유형 은 자신의 사례 대상 이 있 고 그들 자신 도Class유형의 대상 이다.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 류 의 대상 을 얻 을 수 있다.
1.클래스 의 정적 구성원 변 수 를 통 해
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 클래스PropertyDescriptor클래스 는 표준 형식의 자바 빈 이 액세스 기(즉 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);
    }
}

참조 형식
자바 에는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 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, CachedIntrospectionResults>(64);
            //   Class  key,CachedIntrospectionResults  value map,
            //          ,  ConcurrentHashMap    
            CachedIntrospectionResults results = strongClassCache.get(beanClass);
            if (results != null) {
                return results;
            }
            //  strongClassCache    ,  softClassCache   ,softClassCache     
            // softClassCache = new ConcurrentReferenceHashMap, CachedIntrospectionResults>(64);
            // ConcurrentReferenceHashMap Spring       entry     ConcurrentHashMap,        soft,    OOM
            results = softClassCache.get(beanClass);
            if (results != null) {
                return results;
            }
    
            results = new CachedIntrospectionResults(beanClass);
            ConcurrentMap, 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도구 류 는 그 안에 포 함 된 자바 기반 의 지식 점 이 매우 많 고 유형 정보,반사,스 레 드 안전,인용 유형,클래스 로 더 등 을 포함한다.SpringBeanUtils실현 에 서 는ConcurrentHashMap캐 시 로 사용 되 었 으 며,가 져 올 때마다PropertyDescriptor캐 시 에 직접 가서 가 져 올 수 있 으 며,매번 호출native방법 이 필요 없 기 때문에SpringBeanUtils성능 이 좋다.
    링크
    https://segmentfault.com/a/11...

    좋은 웹페이지 즐겨찾기