[spring] 源码简析 aop(配置和注解)

本文从配置文件和注解两个角度介绍spring aop的具体实现。

配置文件方式AOP

spring.xml的配置文件中如下:

1    <aop:config>
2        <aop:aspect ref="aspectTx">
3            <aop:pointcut id="pointcutId" expression="execution(* com.mian.aop.*.*(..))" />
4            <aop:before pointcut-ref="pointcutId" method="start" />
5            <aop:after-returning pointcut-ref="pointcutId"  method="commit" />
6            <aop:after-throwing pointcut-ref="pointcutId" method = "rollback"/>
7        </aop:aspect>
8    </aop:config>

构建BeanDefinition(Advisor)阶段

由前面的文章我们知道,在spring启动的时候,会解析xml文件,将beanDefinition注册到beanFactory中。对于命名空间是aop的节点会通过AopNamespaceHandler来解析节点。
标签和解析器的对应关系如下:

1public class AopNamespaceHandler extends NamespaceHandlerSupport {
2
3    public void init() {
4        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
5        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
6        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
7        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
8    }
9}

config标签最终由ConfigBeanDefinitionParser解析。
通过阅读源码可以知道:

  • 会自动注册AspectJAwareAdvisorAutoProxyCreator的beanDefinition到beanDefinition

  • 解析xml构造出AspectJPointcutAdvisor的RootBeanDefinition和切点的beanDefinition,将其注册到beanFactory中

先来看下结构如下:

构建以AspectJPointcutAdvisor为beanClass的RootBeanDefinition。
由于此RootBeanDefinition是需要注册到beanFactory中的,所以需要beanName。
spring给的命名规则是全类名#couner++自增。
以文章的配置文件为例,这里会创建三个这样的RootBeanDefinition。
分别对应aop:before和aop:after-returing和aop:after-throwing。

它的构造函数的参数是AbstractAspectJAdvice。
也是一个对象,所以构造函数的参数是RootBeanDefinition。

构造函数参数RootBeanDefinition的beanClass取决于配置文件中的定义。
它的属性值aspectName为aop:aspect的值。
构造函数有三个参数Method,切点,aop:aspect对象。
对应的也需要构造三个RootBeanDefinition。

你可能会问,为什么要创建这些BeanDefinition?
因为构建了BeanDefinition就可以根据bean的定义实例化对象了。aop最终就是要给满足切点条件的类创建代理类,就是通过Advisor来增强的。

构造函数参数①methodDef结构如下:

构造的RootBeanDefinition如下:

实现了BeanFactoryAware说明其实例化的过程,能够拿到beanFactory;根据targetBeanName从beanFactory中可以获取到beanDefinition,从而知道beanClass。根据methodName可以获取到具体的Method。

构造函数参数②pointcutDef结构如下:

构造的RootBeanDefinition如下:

这个类中的方法实现直接利用了现成的aspectj,给定express表达式构造出PointcutExpression。
提供matches方法,可以判定一个类是否符合这个切点的条件,或者一个方法是否符合这个切点的条件。

构造函数参数③aspectFactroyDef结构如下:

构造的RootBeanDefinition如下:

实现了BeanFactoryAware,知道aspectBeanName,可以直接获取到aop:aspect实例。

至此,已经构建好了Advisor的BeanDefintion,其中有三个要素(aop:aspect切面,切点,method)。后续在实例化bean的时候,会执行post-processor(比如AspectJAwareAdvisorAutoProxyCreator),是否为bean创建代理对象。

创建代理(利用Advisor)阶段

执行增强的契机是什么?
我们知道,实例化一个对象的时候,会首先根据构造函数实例化,然后设置其PropertyValue,最后会执行用户自定义的init方法。在这过程中有很多的钩子函数。
对bean进行增强的动作就是在init的方法之后。也就是post-processor的下面这段代码:

 1// AbstractAutoProxyCreator
 2    @Override
 3    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 4        if (bean != null) {
 5            Object cacheKey = getCacheKey(bean.getClass(), beanName);
 6            if (!this.earlyProxyReferences.contains(cacheKey)) {
 7                return wrapIfNecessary(bean, beanName, cacheKey);
 8            }
 9        }
10        return bean;
11    }

深入源码可以知道,首先会判断什么样的bean需要增强(参见表格)?

满足需要增强的条件,则开始创建代理,首先需要选择用哪种方式进行代理,我们知道spring提供了两种方式jdk or cglib?

 1    @Override
 2    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
 3        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
 4            Class<?> targetClass = config.getTargetClass();
 5            if (targetClass == null) {
 6                throw new AopConfigException("TargetSource cannot determine target class: " +
 7                        "Either an interface or a target is required for proxy creation.");
 8            }
 9            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
10                return new JdkDynamicAopProxy(config);
11            }
12            return new ObjenesisCglibAopProxy(config);
13        }
14        else {
15            return new JdkDynamicAopProxy(config);
16        }
17    }

下面我们来看Cglib的方式:

 1    @Override
 2    public Object getProxy(ClassLoader classLoader) {
 3        if (logger.isDebugEnabled()) {
 4            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
 5        }
 6
 7        try {
 8            Class<?> rootClass = this.advised.getTargetClass();
 9            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
10
11            Class<?> proxySuperClass = rootClass;
12            if (ClassUtils.isCglibProxyClass(rootClass)) {
13                proxySuperClass = rootClass.getSuperclass();
14                Class<?>[] additionalInterfaces = rootClass.getInterfaces();
15                for (Class<?> additionalInterface : additionalInterfaces) {
16                    this.advised.addInterface(additionalInterface);
17                }
18            }
19
20            // Validate the class, writing log messages as necessary.
21            validateClassIfNecessary(proxySuperClass, classLoader);
22
23            // Configure CGLIB Enhancer...
24            Enhancer enhancer = createEnhancer();
25            if (classLoader != null) {
26                enhancer.setClassLoader(classLoader);
27                if (classLoader instanceof SmartClassLoader &&
28                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
29                    enhancer.setUseCache(false);
30                }
31            }
32            // ⭐️ 需要被代理的类
33            enhancer.setSuperclass(proxySuperClass);
34            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
35            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
36            enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
37           // ⭐️ 增强,这就是利用advisor构建方法调用
38            Callback[] callbacks = getCallbacks(rootClass);
39            Class<?>[] types = new Class<?>[callbacks.length];
40            for (int x = 0; x < types.length; x++) {
41                types[x] = callbacks[x].getClass();
42            }
43            // fixedInterceptorMap only populated at this point, after getCallbacks call above
44            // ⭐️ 这是一个过滤器,哪些方法需要增强
45            enhancer.setCallbackFilter(new ProxyCallbackFilter(
46                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
47            enhancer.setCallbackTypes(types);
48
49            // Generate the proxy class and create a proxy instance.
50            return createProxyClassAndInstance(enhancer, callbacks);
51        }
52        catch (CodeGenerationException ex) {
53        }
54        catch (IllegalArgumentException ex) {
55        catch (Throwable ex) {
56        }
57    }

Cglib通过Enhancer创建代理类:
这里注意三个地方

  1. 给哪个类创建代理类 proxyTargetClass

  2. 执行的时候,哪些方法才需要代理 
    ProxyCallbackFilter.accept 只有返回结果是AOP_PROXY(0)的场合才进行aop代理
    advisors中有任意切点mathches(proxyTargetClas, method),则需要增强。

  3. 方法的执行回调哪些方法 ==> getCallbacks
    重点注意一下 DynamicAdvisedInterceptor.intercept,这里面会将Advisors中切点满足这个method的所有Advisors作为将要被应用的interceptors,来构建反射方法调用(CglibMethodInvocation)。

 1    @Override
 2    public Object proceed() throws Throwable {
 3        //  We start with an index of -1 and increment early.
 4        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
 5            return invokeJoinpoint();
 6        }
 7
 8        Object interceptorOrInterceptionAdvice =
 9                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
10        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
11            // Evaluate dynamic method matcher here: static part will already have
12            // been evaluated and found to match.
13            InterceptorAndDynamicMethodMatcher dm =
14                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
15            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
16                return dm.interceptor.invoke(this);
17            }
18            else {
19                // Dynamic matching failed.
20                // Skip this interceptor and invoke the next in the chain.
21                return proceed();
22            }
23        }
24        else {
25            // It's an interceptor, so we just invoke it: The pointcut will have
26            // been evaluated statically before this object was constructed.
27            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
28        }
29    }
代码的逻辑是这样的:

调用链模式,存在拦截器的场合,依次执行拦截器中的方法,之后回调回到该方法,继续判定是否有拦截器。所有拦截器都执行完毕,则执行方法原生的逻辑,然后一层层回调到拦截器中。

举个例子说明:

 1// AspectJAfterAdvice
 2    @Override
 3    public Object invoke(MethodInvocation mi) throws Throwable {
 4        try {
 5            return mi.proceed();
 6        }
 7        finally {
 8            invokeAdviceMethod(getJoinPointMatch(), null, null);
 9        }
10    }
11
12// MethodBeforeAdviceInterceptor
13    @Override
14    public Object invoke(MethodInvocation mi) throws Throwable {
15        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
16        return mi.proceed();
17    }
这里一前一后两个拦截器,假设添加的拦截器的顺序是先after拦截器,再before拦截器。正确的执行结果,应该是before->原生method->after。

先执行AspectJAfterAdvice中的invoke,由于是后执行增强,所以回到CglibMethodInvocation.proceed。此时拦截器的位置是1,则执行MethodBeforeAdviceInterceptor中的invoke,执行before,然后调回到CglibMethodInvocation.proceed,发现拦截器执行完了,执行原生方法method后,回调到before拦截器中,然后回调到after拦截器中,继续执行after。
拦截器的添加顺序对最终的执行结果是没有影响的。

注解方式AOP

1    <context:component-scan base-package="com.mian.aop"  />
2    <aop:aspectj-autoproxy/>

注解

 1@Aspect
 2@Component(value = "aspectTx")
 3public class TransactionManager {
 4    @Pointcut("execution(* com.mian.aop.*.*(..))")
 5    public void pointcutId(){}
 6
 7    @Before(value = "pointcutId()")
 8    public void start(){
 9        System.out.println("start tx"); 
10    }
11
12    @AfterReturning(value = "pointcutId()")
13    public void commit(){
14        System.out.println("commit tx");
15    }
16
17    @AfterThrowing(value = "pointcutId()")
18    public void rollback(){
19        System.out.println("rollback tx");
20    }
21}

由xml配置文件的方式,我们知道大概的流程是先自动添加处理增强的post-processor,然后构建用于增强的Advisor的BeanDefinition,然后在钩子中给目标对象创建代理。
注解方式的大概流程也是如此。

 1public class AopNamespaceHandler extends NamespaceHandlerSupport {
 2    @Override
 3    public void init() {
 4        // In 2.0 XSD as well as in 2.1 XSD.
 5        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
 6        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
 7        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
 8
 9        // Only in 2.0 XSD: moved to context namespace as of 2.1
10        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
11    }
12}

由上面的代码可以知道,aspect-autoproxy开启自动代理,最终用AspectJAutoProxyBeanDefinitionParser来解析。自动注册了AnnotationAwareAspectJAutoProxyCreator。它和配置文件方式自动注册的post-processor的关系是:

构建BeanDefinition(Advisor)阶段

注解方式是在什么时机构造Adivsor的?

spring容器启动过程中,会将AnnotationAwareAspectJAutoProxyCreator注册到beanFactory中。在实例化其他bean的过程中,会执行post-processors的方法。会判定是否需要给当前的bean创建代理类。判定的逻辑和配置文件aop的方式是一样的。
其中有个判定条件是遍历所有的Advisors,是否有任意的Advisors的切点matches(当前的bean)。该判定条件的前提,就是先要获取所有的Advisors对象。这就是创建的时机。

1// AnnotationAwareAspectJAutoProxyCreator
2    @Override
3    protected List<Advisor> findCandidateAdvisors() {
4        // Add all the Spring advisors found according to superclass rules.
5        List<Advisor> advisors = super.findCandidateAdvisors();
6        // ⭐️ Build Advisors for all AspectJ aspects in the bean factory.
7        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
8        return advisors;
9    }
按照什么逻辑来构造Advisors的?

遍历beanFactory中的所有beanDefinition,找到其中beanClass是被@Aspect注解的beanName,注意这就是aspect切面。找到这个beanClass中没有被@Pointcut注解,但是被以下的注解注解的方法。

  • @Before

  • @Aroud

  • @After

  • @AfterReturing

  • @AfterThrowing

对满足以上条件的Method,一对一创建Advisor。    
我们知道构造Advisors需要以下三信息:

  • 切面aspect实例: 已知aspect的beanName,可以从beanFactory中获取。

  • 切面方法: 上面已知

  • 切点: 方法的注解中value 或者 pointcut的内容。

创建代理(利用Advisor)阶段

在给bean增强这点上,不管是配置文件的方式还是注解的方式,不管是发生的时机还是如何增强的方法都是一样的。

最后

补充一下用于增强的advice,发现其中有三个是直接实现了MethodInterceptor,有两个没有实现,最终是通过适配器,构建成MethodInterceptor。(这么做的原因我暂时没搞清楚) 如下:

总结下,配置文件中的标签,会自动注册的post-processors。
如下:

以上。

 

更多源码分析,关注公众号👇👇👇

 

posted on 2019-04-11 21:12  棉花也是花  阅读(541)  评论(0)    收藏  举报

导航