博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[spring] 源码简析 aop(配置和注解)
阅读量:6688 次
发布时间:2019-06-25

本文共 12055 字,大约阅读时间需要 40 分钟。

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

配置文件方式AOP

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

1    
2
3
4
5
6
7
8

构建BeanDefinition(Advisor)阶段

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

标签和解析器的对应关系如下:

1public class AopNamespaceHandler extends NamespaceHandlerSupport {23    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");1011 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 }1920 // Validate the class, writing log messages as necessary.21 validateClassIfNecessary(proxySuperClass, classLoader);2223 // 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 above44 // ⭐️ 这是一个过滤器,哪些方法需要增强45 enhancer.setCallbackFilter(new ProxyCallbackFilter(46 this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));47 enhancer.setCallbackTypes(types);4849 // 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 have12            // 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 have26            // 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    }1112// MethodBeforeAdviceInterceptor13    @Override14    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    
2

注解

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    }1112    @AfterReturning(value = "pointcutId()")13    public void commit(){14        System.out.println("commit tx");15    }1617    @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.110        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// AnnotationAwareAspectJAutoProxyCreator2    @Override3    protected List
findCandidateAdvisors() {4 // Add all the Spring advisors found according to superclass rules.5 List
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。

如下:

以上。

 

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

 

转载于:https://www.cnblogs.com/mianteno/p/10692620.html

你可能感兴趣的文章
Realm入门指北
查看>>
彻底搞懂Bean加载
查看>>
iOS之传值
查看>>
技术blog 迁移
查看>>
linux 定时器怎么用? crontab 基础
查看>>
React Native - Image
查看>>
Docker和宿主机操作系统文件目录互相隔离的实现原理
查看>>
小程序踩坑系列一
查看>>
探索webpack热更新对代码打包结果的影响(二)
查看>>
微信小程序_豆瓣电影
查看>>
记一次网络模块的小规模重构
查看>>
FMI-人工智能&大数据高峰论坛(深圳站)
查看>>
区块链简单研读笔记
查看>>
为什么 scrum 开发人员是一个 T-形的人 ?
查看>>
使用 CODING 进行 Spring Boot 项目的集成
查看>>
web前端性能优化总结
查看>>
pandas 修改 DataFrame 列名
查看>>
《2018年云上挖矿态势分析报告》发布,非Web类应用安全风险需重点关注
查看>>
leetcode409.Longest Palindrome
查看>>
蚂蚁区块链平台BaaS技术解析与实践
查看>>