SPRING 기술 내막: SPRING 구조 와 디자인 원리 (2 판) - 필기 (6) AOP 차단기 호출 의 실현

6.1 JdkDynamicAopProxy JdkDynamicAopProxy 에이전트 대상 의 리 셋: JdkDynamicAopProxy 는 ReflectiveMethodInvocation 을 통 해 차단기 체인 호출 을 완성 합 니 다.
/** * Implementation of <code>InvocationHandler.invoke</code>. * <p>Callers will see exactly the exception thrown by the target, * unless a hook method throws an exception. */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;
        TargetSource targetSource = this.advised.targetSource;

        Class targetClass = null;
        Object target = null;

        try {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...                
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;

            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.
            /*targetSource.getTarget()                ,target        ,          * ,        HotSwappableTargetSource  ProxyFactoryBean target    。 */
            target = targetSource.getTarget();
            if (target != null) {
                targetClass = target.getClass();
            }

            // Get the interception chain for this method.
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            // Check whether we have any advice. If we don't, we can fallback on direct
            // reflective invocation of the target, and avoid creating a MethodInvocation.
            if (chain.isEmpty()) {
                // 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.
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
            }
            else {
                // We need to create a method invocation...
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                retVal = invocation.proceed();
            }

            // Massage return value if necessary.
            if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                retVal = proxy;
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

6.2 Cglib2aopProxy Cglib2aopProxy 의 intercept 의 리 셋: 내부 클래스 Dynamic Advised Interceptor 의 intercept 방법 으로 리 셋 되 며, CglibMethodInvocation 을 사용 하여 차단기 체인 호출 을 완료 합 니 다.
    /** * General purpose AOP callback. Used when the target is dynamic or when the * proxy is not frozen. */
    private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

        private AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        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 <code>null</code>. 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.  AOP    ,    target     
                    retVal = methodProxy.invoke(target, args);
                }
                else {
                    // We need to create a method invocation...
                    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                }
                retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);
                return retVal;
            }
            finally {
                if (target != null) {
                    releaseTarget(target);
                }
                if (setProxyContext) {
                    // Restore old proxy.
                    AopContext.setCurrentProxy(oldProxy);
                }
            }
        }

        @Override
        public boolean equals(Object other) {
            return (this == other ||
                    (other instanceof DynamicAdvisedInterceptor &&
                            this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
        }

        /** * CGLIB uses this to drive proxy creation. */
        @Override
        public int hashCode() {
            return this.advised.hashCode();
        }

        protected Object getTarget() throws Exception {
            return this.advised.getTargetSource().getTarget();
        }

        protected void releaseTarget(Object target) throws Exception {
            this.advised.getTargetSource().releaseTarget(target);
        }
    }

6.3 목표 대상 방법의 호출 JdkDynamicAopProxy 목표 대상 방법의 호출 은 AopUtils 의 invoke Joinpoint Using Reflection 방법 을 통 해 이 루어 집 니 다.
    /** * Invoke the given target via reflection, as part of an AOP method invocation. * @param target the target object * @param method the method to invoke * @param args the arguments for the method * @return the invocation result, if any * @throws Throwable if thrown by the target method * @throws org.springframework.aop.AopInvocationException in case of a reflection error */
    public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
            throws Throwable {

        // Use reflection to invoke the method.
        try {
            ReflectionUtils.makeAccessible(method);
            return method.invoke(target, args);
        }
        catch (InvocationTargetException ex) {
            // Invoked method threw a checked exception.
            // We must rethrow it. The client won't see the interceptor.
            throw ex.getTargetException();
        }
        catch (IllegalArgumentException ex) {
            throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
                    method + "] on target [" + target + "]", ex);
        }
        catch (IllegalAccessException ex) {
            throw new AopInvocationException("Could not access method [" + method + "]", ex);
        }
    }

}

6.4 차단기 체인 의 호출
JdkDynamicAopProxy 든 Cglib2aopProxy 든 결국은 ReflectiveMethodInvocation 의 proced 방법 으로 차단기 체인 을 호출 하 는 것 입 니 다. advised (class: Advised Support) 는 차단기 체인 을 가지 고 있 습 니 다. getInterceptors AndDynamicInterception Advice 방법의 실현 은 기본 적 인 Advised Support 에서 이 루어 집 니 다.
    /** * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects * for the given method, based on this configuration. * @param method the proxied method * @param targetClass the target class * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers) */
    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;
    }

공장 advisor Chain Factory (impl: Default Advisor Chain Factory) 는 차단기 체인 을 가 져 오 는 과정 을 마 쳤 습 니 다. 그 중에서 Advisor Adapter Registry 는 Proxy Factory Bean 이 XML 파일 에서 얻 은 차단기 의 등록 을 마 쳤 습 니 다. 주로 이전에 설 치 된 List 집합 에 넣 었 습 니 다.list 의 차단 기 는 JdkDynamicaopProxy 의 invoke 나 Cglib2aopProxy 의 intercep 대상 에 의 해 차단 기 를 사용 합 니 다.Default Advisor Chain Factory 의 getInterceptors AndDynamic Interception Advice 방법:
    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);
        boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
        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(targetClass)) {
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    if (MethodMatchers.matches(mm, method, targetClass, 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(targetClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
            else {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }
        return interceptorList;
    }

사실 advisor 는 advisor Support 에서 받 고 초기 화 된 것 이 라 고 알 렸 습 니 다. 그리고 하위 클래스 Proxy Factory Bean 에서 의 실현 은 다음 과 같 습 니 다.
/** * Create the advisor (interceptor) chain. Aadvisors that are sourced * from a BeanFactory will be refreshed each time a new prototype instance * is added. Interceptors added programmatically through the factory API * are unaffected by such changes. */
    private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
        if (this.advisorChainInitialized) {
            return;
        }

        if (!ObjectUtils.isEmpty(this.interceptorNames)) {
            if (this.beanFactory == null) {
                throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
                        "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
            }

            // Globals can't be last unless we specified a targetSource using the property...
            if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
                    this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
                throw new AopConfigException("Target required after globals");
            }

            // Materialize interceptor chain from bean names.
            for (String name : this.interceptorNames) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Configuring advisor or advice '" + name + "'");
                }

                if (name.endsWith(GLOBAL_SUFFIX)) {
                    if (!(this.beanFactory instanceof ListableBeanFactory)) {
                        throw new AopConfigException(
                                "Can only use global advisors or interceptors with a ListableBeanFactory");
                    }
                    addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                            name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
                }

                else {
                    // If we get here, we need to add a named interceptor.
                    // We must check if it's a singleton or prototype.
                    Object advice;
                    if (this.singleton || this.beanFactory.isSingleton(name)) {
                        // Add the real Advisor/Advice to the chain.
                        advice = this.beanFactory.getBean(name);
                    }
                    else {
                        // It's a prototype Advice or Advisor: replace with a prototype.
                        // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
                        advice = new PrototypePlaceholderAdvisor(name);
                    }
                    addAdvisorOnChainCreation(advice, name);
                }
            }
        }

        this.advisorChainInitialized = true;
    }

XML 에 설 정 된 advisor 는 IOC 용기 (DefaultListableBeanFactory) 의 getbean 을 통 해 얻 을 수 있 습 니 다.
6.7 advice 알림 의 실현 위 Default Advisor Chain Factory 의 getInterceptors AndDynamic Interception Advice 가 차단기 체인 을 받 았 을 때 등록 기 (Advisor Adapter Registry registry = GlobalAdvisor Adapter Registry. getInstance () 를 사 용 했 습 니 다. 이 registry 는 AOP 의 주요 핵심 실현 을 많이 포함 하고 있 습 니 다. 이것 은 단일 입 니 다.
/** * Singleton to publish a shared DefaultAdvisorAdapterRegistry instance. * * @author Rod Johnson * @author Juergen Hoeller * @see DefaultAdvisorAdapterRegistry */
public abstract class GlobalAdvisorAdapterRegistry {

    /** * Keep track of a single instance so we can return it to classes that request it. */
    private static final AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();

    /** * Return the singleton DefaultAdvisorAdapterRegistry instance. */
    public static AdvisorAdapterRegistry getInstance() {
        return instance;
    }

}

Default Advisor Adapter Registry 에 adapters 구성원 변 수 를 설정 하 였 습 니 다. 일련의 Advisor Adapter 어댑터 를 놓 았 습 니 다. spring 의 advice 는 어댑터 에 대응 합 니 다. 이것 은 어댑터 의 역할 입 니 다. 하 나 는 adapter 를 호출 하 는 support 방법 입 니 다. 이 방법 을 통 해 얻 은 advice 가 어떤 유형의 advice 에 속 하 는 지 판단 하 는 것 입 니 다.따라서 서로 다른 advice 유형 에 따라 서로 다른 advice interceptor 를 등록 합 니 다. 즉, 앞에서 본 어떤 차단기 입 니까?둘째, 이러한 advice interceptor 는 모두 spring AOP 프레임 워 크 를 디자인 한 것 으로 서로 다른 advice 기능 을 실현 하기 위해 서 비 스 를 제공 하 는 것 이다. 이런 advice interceptor 가 있 으 면 spring 이 제공 하 는 다양한 advice 를 편리 하 게 사용 하여 AOP 응용 을 디자인 할 수 있다. 즉, 이러한 advice interceptor 는 최종 적 으로 advice 알림 이 AOP Proxy 대리 대상 에서 의 짜 임 기능 을 실현 했다.advisorAdaptor 인터페이스 에서 클래스 의 디자인 차원 과 관계: DefaultAdvisor Adapter Registry 코드:
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

    private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);


    /** * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters. */
    public DefaultAdvisorAdapterRegistry() {
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }


    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);
    }

    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            if (adapter.supportsAdvice(advice)) {
            //  adapter support  ,            advice       advice  ,       advice
            //        adviceinterceptor
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
    }

    public void registerAdvisorAdapter(AdvisorAdapter adapter) {
        this.adapters.add(adapter);
    }

}

MethodBefore AdviceAdapter 를 예 로 들 면 원본 코드 는 다음 과 같 습 니 다.
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }

}

코드 에서 볼 수 있 듯 이 슈퍼 ports Advice 방법 은 Advice 가 MethodBefore Advice 유형 인지 아 닌 지 를 판단 하 는 것 입 니 다. 만약 에 위의 Default Advisor Adapter Registry 의 getInterceptors 방법 으로 getInterceptor 방법 을 호출 하여 MethodBefore Advice Interceptor 를 되 돌려 줍 니 다. 이것 이 바로 차단기 체인 에 놓 인 interceptor 입 니 다. 그 코드 는 다음 과 같 습 니 다.
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

    private MethodBeforeAdvice advice;


    /** * Create a new MethodBeforeAdviceInterceptor for the given advice. * @param advice the MethodBeforeAdvice to wrap */
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }
//  invoke            ,                 
    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    }

}

MethodBefore Advice Interceptor 는 advice 를 봉 인 했 습 니 다. methodbeforeadviceinterceptor 가 디자인 한 invoke 리 셋 방법 에서 먼저 advice 를 촉발 한 before 리 셋 을 볼 수 있 습 니 다. 그 다음 에 methodInvocation 의 process 방법 호출 입 니 다. 여 기 를 보면 앞에서 reflectmethodInvocation 의 process 분석 과 연결 되 어 있 습 니 다. 기억 해 보 세 요.AOPCroxy 프 록 시 대상 이 ReflectiveMethodInvocation 을 실행 하 는 process 방법 에서 차단 기 를 얻 은 후에 차단기 invoke 방법 에 대한 호출 을 시작 합 니 다. AOP 의 설정 규칙 에 따라 reflectiveMethodInvocation 이 실행 하 는 차단기 invoke 방법 은 서로 다른 advice 유형 에 따라 spring 이 서로 다른 advice 에 대한 차단기 패 키 징 을 촉발 합 니 다. 예 를 들 어 MethodBeforAdvice 등 입 니 다.최종 적 으로 MethodBeforAdvice Interceptor 방법 을 촉발 합 니 다.MethodBeforAdvice Interceptor 방법 에 서 는 advice 의 before 방법 을 먼저 호출 합 니 다. 이것 이 MethodBeforAdvice 에 필요 한 대상 의 증강 효과 입 니 다. 방법 전에 알림 강 화 를 완료 합 니 다.After Returning AdviceAdapter, Throws AdviceAdapter 는 같은 원리 로 진행 된다.

좋은 웹페이지 즐겨찾기