0x5 Spring系列:深入聊一聊 Spring AOP 实现机制【一】

举报
云享专家 发表于 2019/09/19 16:00:07 2019/09/19
【摘要】 在SpringBoot2.x中最主要的变化就是proxy-target-class默认为true,意味着类代理的时候全部走cglib代理方式,只有为接口代理时才走jdk代理(注意:这里为接口代理,不是指代理目标类是否实现了接口)。所以,在使用springboot2.x的版本中,除了代理目标类是接口外,其余的代理方式全部采用cglib类型。

概述

AOP(Aspect-Oriented Programming) 面向切面编程。Spring Aop 在 Spring框架中的地位举足轻重,主要用于实现事务、缓存、安全等功能。本篇主要是对源码进行深度分析。

·      Spring AOP 多种代理机制相关核心类介绍。

·      Spring Boot 中 AOP 注解方式源码分析。

·      Spring Boot1.x版本和 2.x版本 AOP 默认配置的变动。




一: Spring AOP多种代理机制核心类

 

先介绍一些Spring Aop中一些核心类,大致分为三类:

·      advisorCreator:代理机制,抽象类AbstractAutoProxyCreator的每个实现类代表着一种代理机制。默认情况下只使用一种代理机制。 继承 spring ioc的扩展接口 beanPostProcessor,主要用来扫描获取 advisor。

·      advisor:顾问的意思,advisorSpring中切面的体现形式,封装了spring aop中的切点和通知。

·      advice:通知,也就是 Spring AOP 中增强的方法。

以上三种核心类,来看对应的 UML图。

advisorCreator


·      AbstractAutoProxyCreator:代理机制抽象类,Spring 为Spring AOP 模块暴露的扩展类,也是 AOP 中最核心的类。Nepxion Matrix 框架便是基于此类对AOP进行扩展和增强。

·      BeanNameAutoProxyCreator:根据指定名称创建代理对象(阿里大名鼎鼎的连接池框架druid也基于此类做了扩展)。通过设置 advisor,可以对指定的 beanName 进行代理。支持模糊匹配。

·      AbstractAdvisorAutoProxyCreator:功能比较强大,默认扫描所有Advisor的实现类。相对于根据Bean名称匹配,该类更加灵活。动态的匹配每一个类,判断是否可以被代理,并寻找合适的增强类,以及生成代理类。

·      DefaultAdvisorAutoProxyCreatorAbstractAdvisorAutoProxyCreator的默认实现类。可以单独使用,在框架中使用AOP,尽量不要手动创建此对象。

·      AspectJAwareAdvisorAutoProxyCreator:Aspectj的实现方式,也是Spring Aop中最常用的实现方式,如果用注解方式,则用其子类AnnotationAwareAspectJAutoProxyCreator

·      AnnotationAwareAspectJAutoProxyCreator:目前最常用的AOP使用方式。spring aop 开启注解方式之后,该类会扫描所有@Aspect()标准的类,生成对应的adviosr。目前SpringBoot框架中默认支持的方式,自动配置。




advisor


·      StaticMethodMatcherPointcut:静态方法切面,抽象类。定义了一个classFilter,通过重写getClassFilter()方法来指定切面规则。另外实现了StaticMethodMatcher接口,通过重写matches来指定方法匹配规则。

·      StaticMethodMatcherPointcutAdvisor:静态方法匹配切面顾问,同未抽象类,扩展了切面排序方法。

·      NameMatchMethodPointcut:名称匹配切面,通过指定方法集合变量mappedNames,模糊匹配。

·      NameMatchMethodPointcutAdvisor:方法名称切面顾问,内部封装了
NameMatchMethodPointcut,通过设置方法名称模糊匹配规则和通知来实现切面功能。

·      RegexpMethodPointcutAdvisor:正则表达式切面顾问,可设置多个正则表达式规则,通过内部封装的
JdkRegexpMethodPointcut解析正则表达式。

·      DefaultPointcutAdvisor:默认切面顾问,比较灵活。可自由组合切面和通知。

·      InstantiationModelAwarePointcutAdvisorImplspringboot自动装配的顾问类型,也是最常用的一种顾问实现。在注解实现的切面中,所有@Aspect注释的类,都会被解析成该对象。




advice


·      AspectJMethodBeforeAdvice:前置通知,AspectJ中 before 属性对应的通知(@Before标注的方法会被解析成该通知),在切面方法执行之前执行。

·      AspectJAfterReturningAdvice:后置通知,AspectJ中 afterReturning 属性对应的通知(@AfterReturning 标注的方法会被解析成该通知),在切面方法执行之后执行,如果有异常,则不执行。
注意:该通知与
AspectJMethodBeforeAdvice对应。

·      AspectJAroundAdvice:环绕通知,AspectJ中 around 属性对应的通知(@Around标注的方法会被解析成该通知),在切面方法执行前后执行。

·      AspectJAfterAdvice:返回通知,AspectJ中 after 属性对应的通知(@After 标注的方法会被解析成该通知),不论是否异常都会执行。




可以看出 Spring AOP 提供的实现方式很多,但是殊途同归。

 

具体使用方式已上传至 github:

https://github.com/admin801122/springboot2-spring5-studying





二:Spring Boot 中AOP注解方式源码分析

Spring Aop使用方式很多,从上面的 API 也可以看出。本篇就基于最常用的注解实现方式,对源码深入分析。

@Aspect
@Component
public class LogableAspect {

    
@Pointcut("@annotation(com.springboot2.spring5.springAop.aspect.Logable)")
    
public void aspect() {
    }

    
@Around("aspect()")
    
public Object doAround(ProceedingJoinPoint point) throws Throwable {
        
//...
        Object returnValue =  point.proceed(point.getArgs());
        
//...
        
return returnValue;
    }
}


这是实际项目中,使用AOP最常见的形式,基于注解实现。如今springboot大行其道,我们就从springboot中的EnableAspectJAutoProxy自动配置开始。




大致流程主要分为三个步骤: 一: 创建AnnotationAwareAspectJAutoProxyCreator对象 二: 扫描容器中的切面,创建PointcutAdvisor对象 三: 生成代理类




分别来分析以上三个步骤。

1: 创建 AbstractAutoProxyCreator 对象

基于注解实现的 AOP 默认注册 AbstractAutoProxyCreator 对象实现类为:AnnotationAwareAspectJAutoProxyCreator,来看一下该对象的创建过程。先从spring.factories开始:

Auto Configure
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
        AnnotatedElement.
class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public 
class AopAutoConfiguration {

    
@Configuration
    
@EnableAspectJAutoProxy(proxyTargetClass = false)
    
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
    public 
static class JdkDynamicAutoProxyConfiguration {

    }

    
@Configuration
    
@EnableAspectJAutoProxy(proxyTargetClass = true)
    
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
    public 
static class CglibAutoProxyConfiguration {

    }

}


具体来看:
(1)该配置类的加载前提是什么?

@ConditionalOnClass({ EnableAspectJAutoProxy.classAspect.classAdvice.class,
        
AnnotatedElement.class })


条件注解依赖的配置类均被引入到spring-boot-starter-aop中,只需引入该依赖即可自动装配。
而且可以看到
spring.aop.auto默认为true,并不需要手动开启。
所以很多同学在
springboot项目中使用 aop 的时候,习惯在启动类上引入@EnableAspectJAutoProxy,其实完全不必要。保证项目中有spring-boot-starter-aop依赖即可。

(2)上述代码通过spring.aop.proxy-target-class变量来控制proxyTargetClass的变量,最终都会加载@EnableAspectJAutoProxy配置。
spring.aop.proxy-target-class默认为true,该变量相当关键,控制 spring aop 代理类的生成方式,具体后面详细介绍。




继续跟进EnableAspectJAutoProxy

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    
@Override
    
public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) 
{


        //注册 AnnotationAwareAspectJAutoProxyCreator
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        
// aop 代理方式相关的变量设置到 AopConfigUtils,创建代理类时会读取变量
        
if (enableAspectJAutoProxy != null) {
            
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}
    @Nullable
    public 
static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
            
@Nullable Object source) {

        
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }


上述代码可以看到注册了一个切面相关BeanDefinition,正是上面提到的类:
AnnotationAwareAspectJAutoProxyCreator,并设置了代理方式配置变量: proxyTargetClass,默认为true。

这里只是创建BeanDefinition,并没有实例化和初始化该对象。那什么时候会触发呢?
上面的 uml 图可以看到,该类继承的顶层接口为 
BeanPostProcessor。我们知道BeanPostProcessor实现类会提前初始化,由PostProcessorRegistrationDelegate触发,具体细节之前博客有提到:
SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

该类又继承BeanFactoryAware,所以在其在实例化 bean 后,会触发setBeanFactory()方法,最终会触发
initBeanFactory方法:

    @Override
    
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        
super.initBeanFactory(beanFactory);
        
if (this.aspectJAdvisorFactory == null) {
            
//advisor 工厂类
            
this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
        }
        
//用于创建 advisor
        
this.aspectJAdvisorsBuilder =
                
new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
    }


最终 Advisor 对象就是由aspectJAdvisorsBuilder 生成。

至此,AnnotationAwareAspectJAutoProxyCreator BeanDefinition创建完毕。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。