Spring 에이전트 생 성 및 에이전트 구현

글 목록
  • 1. 대리 공장 초기 화: ProxyFactory
  • 2. 대리 실현
  • 2.1 대리 방식 선택 (cglib, jdk)
  • 2.2 대 리 를 실현 하 는 두 가지 방식
  • 3.1 JdkDynamicAopProxy
  • 4.1 getProxy
  • 4.2 invoke
  • 5.1 차단기 체인 가 져 오기
  • 5.2 차단기 체인 처리
  • 3.2 CglibAopProxy
  • 4.1 Enhancer 를 만 들 고 속성 설정
  • 4.2 차단기 체인 가 져 오기
  • 전제: 프 록 시 를 실현 하기 전에 클래스, 방법 에 대응 하 는 차단기 배열: specificInterceptors.
    Spring AOP 와 Spring 업무 의 실현 은 모두 대 리 를 바탕 으로 하기 때문에 이 부분 을 추출 하여 참고 하 시기 바 랍 니 다.
    1. 대리 공장 초기 화: ProxyFactory
    protected Object createProxy(
            Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
            //.................. 
            //    
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.copyFrom(this);
            //    targetClass        
            if (!proxyFactory.isProxyTargetClass()) {
                if (shouldProxyTargetClass(beanClass, beanName)) {
                    proxyFactory.setProxyTargetClass(true);
                } else {
                    //       ,        
                    evaluateProxyInterfaces(beanClass, proxyFactory);
                }
            }
            //                 Advisor
    		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    		for (Advisor advisor : advisors) {
    			proxyFactory.addAdvisor(advisor);
    		}
    		//    
    		return proxyFactory.getProxy(getProxyClassLoader());
            //................... 
        }
    

            차단 기 는 Advisor 의 적합 한 처리 로 봉 인 됩 니 다. 이쪽 을 보십시오.
    	@Override
    	public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    		if (adviceObject instanceof Advisor) {
    			return (Advisor) adviceObject;
    		}
    		if (!(adviceObject instanceof Advice)) {
    			throw new UnknownAdviceTypeException(adviceObject);
    		}
    		Advice advice = (Advice) adviceObject;
    		if (advice instanceof MethodInterceptor) {
    			// So well-known it doesn't even need an adapter.
    			return new DefaultPointcutAdvisor(advice);
    		}
    		for (AdvisorAdapter adapter : this.adapters) {
    			// Check that it is supported.
    			if (adapter.supportsAdvice(advice)) {
    				return new DefaultPointcutAdvisor(advice);
    			}
    		}
    		throw new UnknownAdviceTypeException(advice);
    	}
    

    getProxy 방법 은 주로 두 가지 절 차 를 포함한다.
  • 프 록 시 초기 화 처리 클래스 가 져 오기: AopProxy
  • 생 성 에이전트
  • 	public Object getProxy(ClassLoader classLoader) {
    		return createAopProxy().getProxy(classLoader);
    	}
    

    2. 대리 실현
    2.1 에이전트 선택 (cglib, jdk)
            프 록 시 를 만 드 는 방법 createProxy 는 설정 및 계승 여부 에 따라 각각 Jdk 또는 Cglib 프 록 시 를 생 성 합 니 다.
    protected Object createProxy(
            Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    		//....................... 
            proxyFactory.setFrozen(this.freezeProxy);
            if (advisorsPreFiltered()) {
                proxyFactory.setPreFiltered(true);
            }
            //       
            return proxyFactory.getProxy(getProxyClassLoader());
        }
    
        public Object getProxy(ClassLoader classLoader) {
            /**
             * @see JdkDynamicAopProxy#getProxy(ClassLoader)  
             * @see CglibAopProxy#getProxy 
             */
            return createAopProxy().getProxy(classLoader);
        }
    
        @Override
        public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            /**
             *       CGLIB  、       、       
             *   CGLIB      
             *     JDK    
             *        AopProxy  ,          
             */
            if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
                Class<?> targetClass = config.getTargetClass();
                if (targetClass == null) {
                    throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
                }
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                    return new JdkDynamicAopProxy(config);
                }
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                return new JdkDynamicAopProxy(config);
            }
        }
    

    2.2 대리 실현 의 두 가지 방식
    3.1 JdkDynamicAopProxy
            JDK 대 리 는 주로 인터페이스 에이전트 에 인용 되 는데 그 는 Invocation Handler 인 터 페 이 스 를 계승 하여 getProxy, Invoke 방법 을 실현 해 야 한다.
    4.1 getProxy
            일반적인 쓰기: getProxy
        /**
         *     JDK           ,    InvocationHandler ,   invoke  ,   proxy   。
         *      invoke  
         * @see #invoke(Object, Method, Object[])
         */
        @Override
        public Object getProxy(ClassLoader classLoader) {
    
            if (logger.isDebugEnabled()) {
                logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
            }
            Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
            findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
            return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
        }
    
    

    4.2 invoke
    이쪽 의 invoke 방법 은 주로 이 몇 단계 로 나 뉜 다.
  • equals, hash, method 소속 클래스 는 인터페이스 처리
  • 목표 대상 내부 의 자기 호출 은 절단면 강 화 를 실시 할 수 없습니다. 이 매개 변 수 를 통 해 대리 노출
  • 차단기 체인 의 처리 (공, 비 공) - 클래스 와 방법 명
  • 결과 복귀
  •  @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //........... 
            try {
                /**
                 * equals、hash、method         
                 */
                if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                   //................... 
                }
    
                //                   ,         
                if (this.advised.exposeProxy) {
                    // Make invocation available if necessary.
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
                //..................... 
    
                /**
                 *            
                 *                 Map>     .
                 */
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                
                if (chain.isEmpty()) {
                    /**
                     *         ,    method.invoke    
                     */
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
                }
                else {
                    /**
                     *              invocation,     invocation
                     * @see ReflectiveMethodInvocation#proceed()
                     */
                    invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                    // Proceed to the joinpoint through the interceptor chain.
                    retVal = invocation.proceed();
                }
    
                //    
                Class<?> returnType = method.getReturnType();
                //............. 
                return retVal;
            }
            finally {
               //.................... 
            }
        }
    

    5.1 차단기 체인 가 져 오기
    여기 서 볼 수 있 습 니 다. 차단기 체인 을 가 져 오 는 방법 은?
  • 캐 시 를 통 해 가 져 오기
  • 캐 시 존재
  • 배열 로 돌아 가기
  • 캐 시가 존재 하지 않 습 니 다.
  • MethodMatcher 를 통 해 일치 하 는 차단 기 를 찾 습 니 다. 구체 적 인 상황 은 구체 적 으로 분석 하고 대체적으로 주 해 를 해석 하 는 방식 으로 찾 습 니 다

  • 	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
    		MethodCacheKey cacheKey = new MethodCacheKey(method);
    		List<Object> cached = this.methodCache.get(cacheKey);
    		if (cached == null) {
    			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
    					this, method, targetClass);
    			this.methodCache.put(cacheKey, cached);
    		}
    		return cached;
    	}
    
    @Override
    	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
    			Advised config, Method method, Class<?> targetClass) {
    
    		// This is somewhat tricky... We have to process introductions first,
    		// but we need to preserve order in the ultimate list.
    		List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
    		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    		boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
    		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    
    		for (Advisor advisor : config.getAdvisors()) {
    			if (advisor instanceof PointcutAdvisor) {
    				// Add it conditionally.
    				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
    				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
    					MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
    					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
    					if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
    						if (mm.isRuntime()) {
    							// Creating a new object instance in the getInterceptors() method
    							// isn't a problem as we normally cache created chains.
    							for (MethodInterceptor interceptor : interceptors) {
    								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
    							}
    						}
    						else {
    							interceptorList.addAll(Arrays.asList(interceptors));
    						}
    					}
    				}
    			}
    			else if (advisor instanceof IntroductionAdvisor) {
    				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
    				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
    					Interceptor[] interceptors = registry.getInterceptors(advisor);
    					interceptorList.addAll(Arrays.asList(interceptors));
    				}
    			}
    			else {
    				Interceptor[] interceptors = registry.getInterceptors(advisor);
    				interceptorList.addAll(Arrays.asList(interceptors));
    			}
    		}
    
    		return interceptorList;
    	}
    

    5.2 차단기 체인 처리
            차단기 체인 이 비어 있 지 않 으 면 일치 하 는 모든 차단기 체인 (method 의 invoke 호출) 을 계속 실행 합 니 다.
        @Override
        public Object proceed() throws Throwable {
            //	We start with an index of -1 and increment early.
            if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }
    
            /**
             *             
             */
            Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                /**
                 *              ,                    
                 */
                InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
                if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                    return dm.interceptor.invoke(this);
                }
                else {
                    /**
                     *      
                     */
                    return proceed();
                }
            }
            else {
                /**
                 *          invoke
                 */
                return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    

    3.2 CglibAopProxy
            cglib 는 Enhancer 류 를 통 해 사용 해 야 하기 때문에 이쪽 의 조작 도 이런 종 류 를 구축 하여 구체 적 인 조작 을 실현 합 니 다.
    4.1 Enhancer 를 만 들 고 속성 설정
        @Override
        public Object getProxy(ClassLoader classLoader) {
          	
            try {
                //................. 
    
                //  Enhancer     ,   CGLB       --    
                Enhancer enhancer = createEnhancer();
                //.......    , 
    
                //         
                Callback[] callbacks = getCallbacks(rootClass);
                //....................... 
    
                // Enhancer  
                return createProxyClassAndInstance(enhancer, callbacks);
            }
            catch (CodeGenerationException ex) {
              // .................. 
            }
        }
    

    4.2 차단기 체인 가 져 오기
            이 인 터 페 이 스 는 Spring 이 지원 하 는 것 으로 그의 intercept 방법 을 실현 하면 대리 의 목적 을 실현 할 수 있 습 니 다.
        private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
            //............ 
            /**
             * 66.      ,DynamicAdvisedInterceptor   Methodlnterceptor,     intercept     CGLB  
             * @see DynamicAdvisedInterceptor#intercept(Object, Method, Object[], MethodProxy)
             */
            Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
     		//............ 
            //       Callback   
            Callback[] mainCallbacks = new Callback[] {aopInterceptor, targetInterceptor, new SerializableNoOp(),  targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised),new HashCodeInterceptor(this.advised)};
    		//....................... 
            return callbacks;
        }
    

            즉, 여기 서 되 돌아 오 는 콜백 리 셋 배열 은 사실 Dynamic Advised Interceptor 의 실현 입 니 다. 콜백 배열 을 Enhancer 에 설정 하여 호출 을 돕 습 니 다.        Dynamic Advised Interceptor 는 MethodInterceptor 인 터 페 이 스 를 실현 하고 intercept 방법 을 실현 하 였 습 니 다. 여기 있 는 itercept 의 실현 은 JDK 대리 와 같 습 니 다.
    		@Override
    		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    			Object oldProxy = null;
    			boolean setProxyContext = false;
    			Class<?> targetClass = null;
    			Object target = null;
    			try {
    				if (this.advised.exposeProxy) {
    					// Make invocation available if necessary.
    					oldProxy = AopContext.setCurrentProxy(proxy);
    					setProxyContext = true;
    				}
    				// May be null. Get as late as possible to minimize the time we
    				// "own" the target, in case it comes from a pool...
    				target = getTarget();
    				if (target != null) {
    					targetClass = target.getClass();
    				}
    				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    				Object retVal;
    				// Check whether we only have one InvokerInterceptor: that is,
    				// no real advice, but just reflective invocation of the target.
    				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
    					// We can skip creating a MethodInvocation: just invoke the target directly.
    					// Note that the final invoker must be an InvokerInterceptor, so we know
    					// it does nothing but a reflective operation on the target, and no hot
    					// swapping or fancy proxying.
    					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    					retVal = methodProxy.invoke(target, argsToUse);
    				}
    				else {
    					// We need to create a method invocation...
    					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    				}
    				retVal = processReturnType(proxy, target, method, retVal);
    				return retVal;
    			}
    			finally {
    				if (target != null) {
    					releaseTarget(target);
    				}
    				if (setProxyContext) {
    					// Restore old proxy.
    					AopContext.setCurrentProxy(oldProxy);
    				}
    			}
    		}
    

    좋은 웹페이지 즐겨찾기