Spring技术内幕:Spring AOP的实现原理(四)

4、目标方法的调用

如果没有拦截器会对目标对象方法直接调用。对于JDKDynamicAopProxy代理对象是通过AopUtils使用反射机制实现的。在这个调用方法中首先得到调用方法的反射对象,然后使用invoke启动对方法反射对象的调用。源码如下:

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

5、AOP拦截器的调用

下面进入AOP的核心部分,Aop是怎样完成对目标的增强的。这些都封装在Aop拦截器链中,由一个具体的拦截器完成。

无论是使用JDKDynamicAopProxy还是使用CglibAopProxy创建代理对象最终对AOP拦截链的调用都是在ReflectiveMethodInvocation中通过proceed方法实现的。在proceed方法中逐个运行拦截器的拦截方法。在运行拦截器的拦截方法之前需要对代理方法完成一个匹配,通过这个匹配判断来决定拦截器是否满足切面增强的要求。具体代码如下:

    public Object proceed() throws Throwable {
        //  We start with an index of -1 and increment early.
        // 从索引为-1的拦截器开始调用,并按序递增
        // 如果拦截器链里的拦截器迭代调用完毕,这里开始调用target函数,这个函数是通过反射机制完成
        // 具体在AopUtils.invokeJoinpointUsingReflection方法中完成
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }
        // 这里沿着定义好的interceptorOrInterceptionAdvice链进行处理
        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        // 这里对拦截器进行动态匹配判断
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            // 前面分析的Pointcut,这里是触发匹配的地方,如果定义的Pointcut匹配,那么这个advice将会得到执行
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                // 如果不匹配,proceed会递归调用,知道所有的拦截器被运行
                return proceed();
            }
        }
        else {
            // It‘s an interceptor, so we just invoke it: The pointcut will have
            // been evaluated statically before this object was constructed.
            // 如果是一个拦截器,直接调用这个interceptor对应的方法
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

这时我们会有疑问,这些advisor是怎样从配置文件中获得并配置到proxy的拦截器链中,我们使用的advisor通知时怎样起作用的,让我们带着这些问题继续往下看。

6、配置通知器

在整个AopProxy代理对象拦截回调过程中,先回到ReflectionMethodInvocation类的proceed方法,在这个方法里,可以看到得到了配置的interceptorOrInterceptionAdvice,如下所示:

Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

interceptorOrInterceptionAdvice是获得的拦截器,他通过拦截器机制对目标对象进行增强。这个拦截器来自interceptorsAndDynamicMethodMatchers。具体来说,他是interceptorsAndDynamicMathers持有的List中的一个元素。关于如何配置拦截器的问题就转化为了List中的拦截器元素是从哪里来的,在哪里配置的问题。接着对invoke调用进行回放,回到JDKDynamicAopProxy中的invoke方法中,可以看到这个List中的interceptors是从哪个调用中获取的。对于CglibAopProxy,也有类似过程,只不过这个过程是在DynamicAdvisedInterceptor的intercept回调中实现的,如下所示:

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);

从上面的代码可以看出,获取intercptors的操作是由advised对象完成的,这个advised是一个AdvisedSupport对象,从AdvisedSupport类中可以看到getInterceptorsAndDynamicInterceptionAdvice的实现。在这个方法中取得了拦截器链,再取得拦截器链的时候,为了提高拦截器链的效率,还为这个拦截器链这是了缓存。

    /**
     * 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) {
        // 这里使用cache,利用cache去获取已有的inteceptor链,但是第一次还是需要自己动手生成的。这个inteceptor链的生成
        // 是由advisorChainFactory完成的,在这里使用的是DefaultAdvisorChainFactory
        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;
    }

在 DefaultAdvisorChainFactory中实现了interceptor链的获取过程,在这个过程中,首先设置了一个List,其长度是配置的通知器的个数来决定的,这个配置时在XML中对ProxyFactoryBean做的interceptNames属性的配置,然后,DefaultAdvisorChainFactory会通过一个AdvisorAdapterRegistry来实现拦截器的注册。AdvisorAdapterRegistry对advice通知的织入功能起了很大作用。有了AdvisorAdapterRegistry注册器,利用他来对ProxyFactoryBean配置中得到的通知进行适配,从而得到相应的拦截器,再把他前面设置好的List中去,完成所谓的拦截器注册过程。在拦截器适配和注册过程完成以后,List中的拦截器会被JDK生成的AopProxy代理对象的invoke方法或者CGLIB代理对象的intercept拦截方法获得,并启动拦截器的invoke方法调用,最终触发通知的切面增强。

下面看看DefaultAdvisorChainFactory是怎样生成拦截器链的:

/**
 * A simple but definitive way of working out an advice chain for a Method,
 * given an {@link Advised} object. Always rebuilds each advice chain;
 * caching can be provided by subclasses.
 *
 * @author Juergen Hoeller
 * @author Rod Johnson
 * @author Adrian Colyer
 * @since 2.0.3
 */
@SuppressWarnings("serial")
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
    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.
        // advisor链已经在config中持有了,这里可以直接使用
        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)) {
                    // 拦截器链是通过AdvisorAdapterRegistry来加入的,这个AdvisorAdapterRegistry
                    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;
    }
}
    /**
     * Determine whether the Advisors contain matching introductions.
     */
    // 判断Advisors是否符合要求
    private static boolean hasMatchingIntroductions(Advised config, Class targetClass) {
        for (int i = 0; i < config.getAdvisors().length; i++) {
            Advisor advisor = config.getAdvisors()[i];
            if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (ia.getClassFilter().matches(targetClass)) {
                    return true;
                }
            }
        }
        return false;
    }

在ProxyFactoryBean的getObject方法中对adviosr进行初始化,从XML配置中获取了advisor通知器。下面看下在ProxyFactoryBean拦截器链的初始化中获取advisor通知器

    /**
     * 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.
                        // 这里是取得advisor的地方,是通过BeanFactory取得的
                        // 把interceptorNames这个List中interceptor名字交给
                        // BeanFactory,然后通过调用BeanFactory的getBean去获取
                        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;
    }

advisor通知器的取得时委托给IOC容器完成的,但是在ProxyFactoryBean中是如何获得IOC容器,然后通过回调IOC容器的getBean方法来得到需要的通知advisor?在这里大家可以回顾下IOC容器的原理。

未完待续……

时间: 2024-10-07 09:32:20

Spring技术内幕:Spring AOP的实现原理(四)的相关文章

Spring技术内幕——Spring的设计理念和整体架构

横看成岭侧成峰,远近高低各不同. 不识庐山真面目,只缘身在此山中. --苏轼 Spring的各个子项目 1.Spring Framework(Core):Spring项目的核心.包含了一系列IOC容器的设计,提供了反转模式的实现,同时还集成了AOP功能.另外,在Spring Framework中,还包含了其他Spring的基本模块,比如MVC.JDBC.事务处理模块的实现. 2.Spring Web Flow:建立在Spring MVC基础上的Web工作流引擎.定义了一种特定的语言来描述工作流,

Spring技术内幕——Spring Framework的IOC容器实现(一)

一.SpringIOC容器概述 IOC容器和依赖反转的模式 在面向对象的系统中,对象封装了数据和对数据的处理,对象的依赖关系常常体现在对数据和方法的依赖上.这些依赖关系可以通过把对象的依赖注入交给框架IOC容器来完成.他可以再解耦代码的同时提高了代码的可测试性. 依赖控制反转的实现由很多种方式,在Spring中,IOC容器是实现这个模式的载体,他可以再对象生成或者初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中的方式来注入对方法调用的依赖.这种依赖注入是可以递归的,对象被逐

Spring技术内幕——Spring Framework的IOC容器实现(三)

接上一篇的时序图.这里调用的loadBeanDefintions实际上是一个抽象方法,那么实际载入过程发生在哪里呢?在loadBeanDefintions中,初始化了读取器XMLBeanDefinitionReader,然后把这个读取器在IOC容器中设置好(过程和编程式使用XMLBeanFactory是类似的),最后是启动读取器来完成BeanDefinition在IOC容器中的载入,代码如下: /** * Convenient base class for {@link org.springfr

Spring技术内幕——Spring Framework的IOC容器实现(二)

三.IOC容器的初始化过程 IOC容器的初始化时由前面介绍的refresh方法来启动的,这个方法标志着IOC容器的正式启动.这个启动包括BeanDefinition的Resource定位.载入和注册.下面我们将详细分析这三个实现过程,Spring把这三个过程分开,并使用不同的模块来完成,通过这样的设计让用户更加灵活的这三个过程进行剪裁和扩展,定义出最适合自己的IOC容器的初始化过程. 第一个过程: Resource定位过程,是指BeanDefinition的资源定位,他由ResourceLoad

Spring技术内幕——Spring Framework的IOC容器实现(五)(大结局)

这里通过使用BeanDefinitionResolver来对BeanDefinition进行解析,然后注入到property中.下面到BeanDefinitionValueResolver中看一下解析过程,以对Bean reference进行解析为例 /** * Resolve a reference to another bean in the factory. * class BeanDefinitionValueResolver */ private Object resolveRefer

深入探索spring技术内幕(二): 剖析spring管理Bean的原理与配置

求二叉树的宽度和深度 给定一个二叉树,获取该二叉树的宽度和深度. 例如输入 a / \ b c / \ / \ d e f g 返回3. 详细描述: 接口说明 原型: int GetBiNodeInfo(BiNode &head, unsigned int *pulWidth, unsigned int *pulHeight) 输入参数: head 需要获取深度的二叉树头结点 输出参数(指针指向的内存区域保证有效): pulWidth 宽度 pulHeight 高度 返回值: 0 成功 1 失败

深入探索spring技术内幕(四): 剖析@Resource注解实现原理与注解注入

一.@Resource注解原理 @Resource可以标注在字段或属性的setter方法上 1.  如果指定了name属性, 那么就按name属性的名称装配; 2. 如果没有指定name属性, 那就按照默认的名称查找依赖对象; 3. 如果按默认名称查找不到依赖对象, 那么@Resource注解就会回退到按类型装配; ① 先写一个自己的@MyResource: import java.lang.annotation.Retention; import java.lang.annotation.Re

深入探索spring技术内幕(七): 配置Spring AOP面向切面编程

一. AOP一些概念 Aspect( 切面 ): 指横切性关注点的抽象即为切面, 它与类相似, 只是两者的关注点不一样, 类是对物体特征的抽象, 而切面横切性关注点的抽象. joinpoint( 连接点 ): 指那些被拦截到的点. 在spring中, 这些点指的是方法, 因为spring只支持方法类型的连接点, 实际上joinpoint还可以是field或类构造器) Pointcut( 切入点 ): 指我们要对那些joinpoint进行拦截的定义. Advice( 通知 ): 指拦截到joinp

Spring技术内幕:SpringIOC原理学习总结

前一段时候我把Spring技术内幕的关于IOC原理一章看完,感觉代码太多,不好掌握,我特意又各方搜集了一些关于IOC原理的资料,特加深一下印象,以便真正掌握IOC的原理. IOC的思想是:Spring容器来实现这些相互依赖对象的创建.协调工作.对象只需要关系业务逻辑本身就可以了. SpringIOC容器的执行步骤是: 1.资源定位,即首先要找到applicationContext.xml文件 2.BeanDefinition的载入,把XML文件中的数据统一加载到BeanDefinition中,方

Spring技术内幕:设计理念和整体架构概述

程序员都很崇拜技术大神,很大一部分是因为他们发现和解决问题的能力,特别是线上出现紧急问题时,总是能够快速定位和解决. 一方面,他们有深厚的技术基础,对应用的技术知其所以然,另一方面,在采坑的过程中不断总结,积累了很多经验. 相信大家都使用过Spring,有些人了解它的核心:IOC和AOP,但只是了解它们的基本概念.使用了反射和动态代理,关于如何管理对象.代理的具体实现了解的比较浅. 有些人使用Spring MVC,使用Spring集成数据库.事务.消息队列以简化操作,但对集成的具体设计思路和实现