Springソースコード学習の認識AOP

45544 ワード

前言
Springの重要な特性の一つとして、AOPの役割はもちろんです.それはコード実行中にコードを横断して、繰り返し実行する操作をカプセル化できるようにします.コードのモジュール化と管理時の利便性を保証します.このブログはソースの角度からAOPの仕事原理と使用過程を解析します.
簡単なAOP実現

<bean id="testadvice" class="chapter3_AOP.TestAdvice">bean>
<bean id="aop" class="org.springframework.aop.framework.ProxyFactoryBean">
        
        <property name="proxyInterfaces">
            <value>chapter3_AOP.OperateApivalue>
        property>
        <property name="target">
            <bean class="chapter3_AOP.OperateTarget">bean>
        property>
        <property name="interceptorNames">
            <list>
                <value>testadvicevalue>
            list>
        property>
    bean>
// OperateTarget.java 
public class OperateTarget implements OperateApi{
    @Override
    public void update() {
        System.out.println("update");
    }
}
// TestAdvice.java
public class TestAdvice implements MethodBeforeAdvice{

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("prehandle");
    }

}
// client.java
public class client {
    public static void main(String []args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        OperateApi api = (OperateApi)ac.getBean("aop");
        api.update();
    }
}
//   
prehandle
update
AOPフロー
1.プロキシオブジェクトの生成
ProxyFactoryBeanの実現はSpring IoC環境においてAOPを作成するための一番下の方法であり、最も柔軟な方法でもある.だからProxyFactoryBeanの使用を通して、AOPの運行がどのような過程なのかを見てみます.
// ProxyFactoryBean.java
//          
public Object getObject() throws BeansException {
        initializeAdvisorChain();
        if (isSingleton()) {
            return getSingletonInstance();
        }
        else {
            if (this.targetName == null) {
                logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
            }
            return newPrototypeInstance();
        }
    }

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

            //         
            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");
                    }
                  //          ,      beanfactory    advisor interceptor,
                  //    advisors 
                    addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                            name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
                }

                else {
                    Object advice;
                    if (this.singleton || this.beanFactory.isSingleton(name)) {
                        //        beanfactory       
                        advice = this.beanFactory.getBean(name);
                    }
                    else {
                        advice = new PrototypePlaceholderAdvisor(name);
                    }
                    addAdvisorOnChainCreation(advice, name);
                }
            }
        }

        this.advisorChainInitialized = true;
    }

//      
private void addAdvisorOnChainCreation(Object next, String name) {
        //         advisor。   advice, advice          advisor
        Advisor advisor = namedBeanToAdvisor(next);
        if (logger.isTraceEnabled()) {
            logger.trace("Adding advisor with name '" + name + "'");
        }
        addAdvisor(advisor);
    }

//       
private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            //      targetName        targetSource 
            this.targetSource = freshTargetSource();
            if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
                //   Class
                Class> targetClass = getTargetClass();
                if (targetClass == null) {
                    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                }
                //          ,         
                setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
            }
            // Initialize the shared singleton instance.
            super.setFrozen(this.freezeProxy);
            //          
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    }


private synchronized Object newPrototypeInstance() {
        // In the case of a prototype, we need to give the proxy
        // an independent instance of the configuration.
        // In this case, no proxy will have an instance of this object's configuration,
        // but will have an independent copy.
        if (logger.isTraceEnabled()) {
            logger.trace("Creating copy of prototype ProxyFactoryBean config: " + this);
        }

        ProxyCreatorSupport copy = new ProxyCreatorSupport(getAopProxyFactory());
        // The copy needs a fresh advisor chain, and a fresh TargetSource.
        TargetSource targetSource = freshTargetSource();
        copy.copyConfigurationFrom(this, targetSource, freshAdvisorChain());
        if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
            // Rely on AOP infrastructure to tell us what interfaces to proxy.
            copy.setInterfaces(
                    ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));
        }
        copy.setFrozen(this.freezeProxy);

        if (logger.isTraceEnabled()) {
            logger.trace("Using ProxyCreatorSupport copy: " + copy);
        }
        return getProxy(copy.createAopProxy());
    }

    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

    // DefaultAopProxyFactory.java
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        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.");
            }
            //             ,  jdk    
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

//   aopPorxy              object  
protected Object getProxy(AopProxy aopProxy) {
        return aopProxy.getProxy(this.proxyClassLoader);
    }
    //AdvisorSupport.java
    //              advisor   advisors   ,      
    @Override
    public void addAdvisor(Advisor advisor) {
        int pos = this.advisors.size();
        addAdvisor(pos, advisor);
    }

    @Override
    public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
        if (advisor instanceof IntroductionAdvisor) {
            validateIntroductionAdvisor((IntroductionAdvisor) advisor);
        }
        addAdvisorInternal(pos, advisor);
    }

    private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
        Assert.notNull(advisor, "Advisor must not be null");
        if (isFrozen()) {
            throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
        }
        if (pos > this.advisors.size()) {
            throw new IllegalArgumentException(
                    "Illegal position " + pos + " in advisor list with size " +         this.advisors.size());
        }
        this.advisors.add(pos, advisor);
        updateAdvisorArray();
        adviceChanged();
    }

    /**
     * Bring the array up to date with the list.
     */
    protected final void updateAdvisorArray() {
        this.advisorArray = this.advisors.toArray(new Advisor[this.advisors.size()]);
    }
    protected void adviceChanged() {
        this.methodCache.clear();
    }
以下はjdkダイナミックエージェントとcglibダイナミックエージェントの2つのプロキシ方式でプロキシオブジェクトを生成するプロセスである.
  • jdkダイナミックエージェント
    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);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
            //                   
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
  • cglib動的エージェント
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
        }
    
        try {
            Class> rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
    
            Class> proxySuperClass = rootClass;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class>[] additionalInterfaces = rootClass.getInterfaces();
                for (Class> additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }
    
            // Validate the class, writing log messages as necessary.
            validateClassIfNecessary(proxySuperClass, classLoader);
    
            // Configure CGLIB Enhancer...
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }
            enhancer.setSuperclass(proxySuperClass);
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
    
            Callback[] callbacks = getCallbacks(rootClass);
            Class>[] types = new Class>[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            // fixedInterceptorMap only populated at this point, after getCallbacks call above
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);
    
            // Generate the proxy class and create a proxy instance.
            return createProxyClassAndInstance(enhancer, callbacks);
        }
        catch (CodeGenerationException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                    this.advised.getTargetClass() + "]: " +
                    "Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (IllegalArgumentException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                    this.advised.getTargetClass() + "]: " +
                    "Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (Exception ex) {
            // TargetSource.getTarget() failed
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }
    以上は、プロキシオブジェクトを生成するためのプロセスであり、ブロッカのステップは、2つのプロキシのコールバックにある.次に、リピートコードの実行を突っ込んで、ブロックの役割を果たします.2.ブロックチェーンを生成するjdk動的エージェントのフィードバックを例にとって、ブロックチェーン
  • を対象としてどのように生成するかを確認する.
    // jdk         
    @Override
        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)) {
                    //          equal,    。
                    return equals(args[0]);
                }
                if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                    //       hashCode()
                    return hashCode();
                }
                if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                        method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                    //            
                    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.
                target = targetSource.getTarget();
                if (target != null) {
                    targetClass = target.getClass();
                }
    
                //             
                List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
                //        ,          
                if (chain.isEmpty()) {
                    //             
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
                }
                else {
                    //     ReflectiveMethodInvocation,          
                    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.
                Class> returnType = method.getReturnType();
                if (retVal != null && retVal == target && returnType.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;
                }
                else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                    throw new AopInvocationException(
                            "Null return value from advice does not match primitive return type for: " + method);
                }
                return retVal;
            }
            finally {
                if (target != null && !targetSource.isStatic()) {
                    // Must have come from TargetSource.
                    targetSource.releaseTarget(target);
                }
                if (setProxyContext) {
                    // Restore old proxy.
                    AopContext.setCurrentProxy(oldProxy);
                }
            }
        }
    // AdvisedSupport.java
    //                
    public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class> targetClass) {
            MethodCacheKey cacheKey = new MethodCacheKey(method);
            List cached = this.methodCache.get(cacheKey);
            if (cached == null) {
                cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                        this, method, targetClass);
                this.methodCache.put(cacheKey, cached);
            }
            return cached;
        }
    // DefaultAdvisorChainFactory.java
    public List 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 interceptorList = new ArrayList(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)) {
                      //   advisor      
                      MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                      //   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;
        }
    // DefaultAdvisorAdapterRegistry.java
    //   adviceadapter     interceptor
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
            List interceptors = new ArrayList(3);
            Advice advice = advisor.getAdvice();
            if (advice instanceof MethodInterceptor) {
                interceptors.add((MethodInterceptor) advice);
            }
            for (AdvisorAdapter adapter : this.adapters) {
                if (adapter.supportsAdvice(advice)) {
                    interceptors.add(adapter.getInterceptor(advisor));
                }
            }
            if (interceptors.isEmpty()) {
                throw new UnknownAdviceTypeException(advisor.getAdvice());
            }
            return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
        }
    // MethodBeforeAdviceAdapter
    @Override
        public boolean supportsAdvice(Advice advice) {
            return (advice instanceof MethodBeforeAdvice);
        }
    
        //   advice     interceptor
        @Override
        public MethodInterceptor getInterceptor(Advisor advisor) {
            MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
            return new MethodBeforeAdviceInterceptor(advice);
        }
    3.adviceでの強化を実現する
    キーは以下の操作により、強化機能の実現を実現する.
        invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        retVal = invocation.proceed();
    // ReflectiveMethodInvocation.java
    protected ReflectiveMethodInvocation(
                Object proxy, Object target, Method method, Object[] arguments,
                Class> targetClass, List interceptorsAndDynamicMethodMatchers) {
    
            this.proxy = proxy;
            this.target = target;
            this.targetClass = targetClass;
            this.method = BridgeMethodResolver.findBridgedMethod(method);
            this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
            this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
        }
    
    public Object proceed() throws Throwable {
            //   index -1    。           ,           
            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)) {
                    //            invoke  
                    return dm.interceptor.invoke(this);
                }
                else {
                    //     ,        
                    return proceed();
                }
            }
            else {
    
                return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    //  methodBeforeAdvice        
    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  ,  advice      before  ,   proceed,          
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
            return mi.proceed();
        }
    
    }
    // AfterReturningAdvice   ,              ,     advice       
    //                  
    public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
    
        private final AfterReturningAdvice advice;
    
    
        /**
         * Create a new AfterReturningAdviceInterceptor for the given advice.
         * @param advice the AfterReturningAdvice to wrap
         */
        public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
            Assert.notNull(advice, "Advice must not be null");
            this.advice = advice;
        }
    
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            Object retVal = mi.proceed();
            this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
            return retVal;
        }
    
    }
    
    これでadviceの強化機能を完成し、aop全体が完成しました.
    結び目
    以上のソースコードの分析を通して、最初から最後まで整理してきました.
  • は、構成情報を取得してadvisorチェーンを生成する.
  • は、状況に応じて、jdk動的エージェントまたはcglib動的エージェントを使用する.
  • は、advisorチェーンにおける接点および拡張(advice)に基づいて、ブロッキングチェーンを生成する.
  • は、ブロックチェーンを再帰的に実行し、動的整合規則が一致するかどうかを判断する.強化に応じて、タンデムでの調整方法を強化します.
  • このとき戻ってきたaopプロキシオブジェクトは、メソッドを呼び出すと、エンハンス順にエンハンティング方法と初期オブジェクト方法を順次呼び出して、AOP強化の機能を実現します.