spring-AOP(二)实现原理之AspectJ注解方式

在上一篇spring-AOP(一)实现原理我们了解了如何使用ProxyFactory来创建AOP代理对象,但其过程需要实现一些接口,并且需要一些比较复杂的配置。因此,在spring2.0之后,提供了一种较为便利的方式。
使用@Aspect注解声明一个切面类,之后通过@EnableAspectJAutoProxy注解来注册代理生成类AnnotationAwareAspectJAutoProxyCreator。下面我们来看一下其原理

spring中如何使用@AspectJ

织入方式

手动织入

  1. 首先需要定义一个Aspect

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    package com.luhc.springaop.aspect;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;

    /**
    * @author luhuancheng
    * @date 2018/11/20
    */
    @Aspect
    public class PerformanceTraceAspect {

    @Pointcut("execution(* *..*method1()) || execution(* *..*method2())")
    public void pointcutName(){}

    @Pointcut("@annotation(AnyJoinpointAnnotation)")
    public void matchPointcut(){}

    @Before("matchPointcut()")
    public void before() {
    System.out.println("+++++++++@annotation++++++++++");
    }

    @Around("pointcutName()")
    public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();

    try {
    return joinPoint.proceed();
    } finally {
    System.out.println(String.format("cost time %s", System.currentTimeMillis() - start));
    }
    }

    }
  2. 通过AspectJProxyFactory手动织入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    private static void manualWeaver() {
    // 手动织入
    AspectJProxyFactory weaver = new AspectJProxyFactory();
    weaver.setProxyTargetClass(true);
    // 声明目标对象
    weaver.setTarget(new Foo());
    // 声明切面
    weaver.addAspect(PerformanceTraceAspect.class);
    // 获取代理
    Object proxy = weaver.getProxy();
    // 执行已经织入切面逻辑的方法
    ((Foo) proxy).method1(new FlyImpl());
    ((Foo) proxy).method2();
    }

自动织入

自动织入方式需要AnnotationAwareAspectJAutoProxyCreator类的支持,通过将AnnotationAwareAspectJAutoProxyCreator和需要的Aspect切面、以及目标对象声明在IOC容器中,在容器启动期间,AnnotationAwareAspectJAutoProxyCreator将自动为目标对象生成织入切面逻辑的代理对象

  1. 声明配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    package com.luhc.springaop.aspect;

    import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    /**
    * @author luhuancheng
    * @date 2018/11/21
    */
    @Configuration
    public class AspectConfigure {

    /**
    * 自动织入器
    * @return
    */
    @Bean
    public AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
    AnnotationAwareAspectJAutoProxyCreator proxyCreator = new AnnotationAwareAspectJAutoProxyCreator();
    // 默认为false,如果目标对象未实现接口的话,其代理对象也是通过cglib生成
    proxyCreator.setProxyTargetClass(false);
    return proxyCreator;
    }

    /**
    * 未实现接口的目标对象
    * @return
    */
    @Bean
    public Foo foo() {
    return new Foo();
    }

    /**
    * 切面
    * @return
    */
    @Bean
    public PerformanceTraceAspect performanceTraceAspect() {
    return new PerformanceTraceAspect();
    }

    }
  2. 从IOC容器中取出织入切面后的代理对象

    1
    2
    3
    4
    5
    6
    7
    private static void autoWeaver() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class);
    Foo foo = context.getBean(Foo.class);
    // 此时的foo对象,是以及经过AnnotationAwareAspectJAutoProxyCreator处理后的代理对象
    foo.method1(new FlyImpl());
    foo.method2();
    }

@AspectJ形式的Pointcut声明方式

Spring AOP支持以下的Pointcut表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 任意包下的具有任意参数、任意返回值的任意方法
// @Pointcut("execution(* *..*(..))")

// com.luhc.springaop的任意子包下的任意类,springAOP只支持方法级别的JoinPoint,因此这个表达式将匹配指定类所声明的所有方法执行
// @Pointcut("within(com.luhc.springaop..*)")

// 匹配代理对象类型为Foo的所有方法级的JoinPoint
// @Pointcut("this(Foo)")

// 匹配目标对象类型为Fly的所有方法级的JoinPoint
// @Pointcut("target(Fly)")

// 匹配传入参数类型为Fly和Foo的所有方法执行的JoinPoint,不关心方法在哪个类中定义
// @Pointcut("args(Fly,Foo)")

// @within @target的区别在于@within是静态匹配、@target是在运行时动态匹配
// 匹配所有被注解AnyJoinpointAnnotation标注了的类的所有方法级的JoinPoint
// @Pointcut("@within(AnyJoinpointAnnotation)")

// 匹配所有目标对象呗注解AnyJoinpointAnnotation标注了的类的所有方法级的JoinPoint
// @Pointcut("@target(AnyJoinpointAnnotation)")

// 匹配方法参数类型被注解AnyJoinpointAnnotation标注了的所有方法级的JoinPoint
// @Pointcut("@args(AnyJoinpointAnnotation)")

// 匹配方法被注解AnyJoinpointAnnotation标注了的所有方法级的JoinPoint
// @Pointcut("@annotation(AnyJoinpointAnnotation)")

// 可以使用 || 和 && 来表达pointcut之间的逻辑运算
// @Pointcut("execution(* *..*method1()) || execution(* *..*method2())")

剖开@AspectJ在SpringAOP中的真相

@AspectJ形式的Pointcut

AnnotationAwareAspectJAutoProxyCreator通过反射获取到@Pointcut注解的信息,在内部实例化为AspectJExpressionPointcut对象。
AspectJExpressionPointcut实现了ClassFilter、MethodMatcher,其内部实现逻辑代理给了PointcutParser,最终生成为PointcutExpression(PointcutExpressionImpl实现类)实例

@AspectJ形式的Advice

在Advice定义中访问Joinpoint处的方法参数

使用org.aspectj.lang.JoinPoint

将Advice方法的第一个参数声明为org.aspectj.lang.JoinPoint类型,我们可以通过调用org.aspectj.lang.JoinPoint相关方法获取需要的数据

1
2
3
4
5
6
@Before("matchPointcut()")
public void before(org.aspectj.lang.JoinPoint joinPoint) {
// 获取方法名
System.out.println(joinPoint.getSignature().getName());
System.out.println("+++++++++@annotation++++++++++");
}

使用args标志符绑定

1
2
3
4
5
6
// 可以同时使用标识符和JoinPoint,但是JoinPoint必须放在第一个参数位置上
@Before("matchPointcut() && args(name)")
public void before(JoinPoint joinPoint, String name) {
System.out.println("获取到Joinpoint上的入参:" + name);
System.out.println("获取到Joinpoint的方法名: " + joinPoint.getSignature().getName());
}

捕获异常@AfterThrowing

1
2
3
4
@AfterThrowing(pointcut = "matchPointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, RuntimeException e) {
System.out.println("方法:" + joinPoint.getSignature().getName() + "发生异常:" + e.getMessage());
}

捕获返回值@AfterReturning

1
2
3
4
@AfterReturning(pointcut = "pointcutName()", returning = "result")
public void afterReturning(JoinPoint joinPoint, String result) {
System.out.println("方法:" + joinPoint.getSignature().getName() + "获得返回值:" + result);
}

方法正常执行完成后(未抛出异常)@After

1
2
3
4
@After("pointcutName()")
public void after(JoinPoint joinPoint) {
System.out.println("方法:" + joinPoint.getSignature().getName() + ": 执行完毕");
}

@Around环绕方法

@Around与其他几个Advice注解不同,在@Around方法中,第一个参数必须为org.aspectj.lang.ProceedingJoinPoint

1
2
3
4
5
6
7
8
9
10
@Around("pointcutName()")
public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();

try {
return joinPoint.proceed();
} finally {
System.out.println(String.format("cost time %s", System.currentTimeMillis() - start));
}
}

公开当前调用的代理对象

1
2
// 在目标对象方法中,通过此方法可以获取当前目标对象的代理对象
AopContext.currentProxy()

@AspectJ形式的Spring AOP代理自动生成原理

一个例子

我们使用注解配置的一个spring aop的demo(新版本的spring中推荐使用注解来配置容器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// 假设这是一个业务接口
public interface Fly {
void fly();
}

// 业务接口实现
public class FlyImpl implements Fly {
@Override
public void fly() {
System.out.println("++++++++++++++++ Fly ++++++++++++++++");
}
}

// 声明一个切面
@Aspect
public class PerformanceTraceAspect {

// 匹配任意返回值、任意包下的、任意参数的fly方法。在这个demo中,将匹配到com.luhc.springaop.aspect.FlyImpl#fly这个方法
@Pointcut("execution(* *..*fly(..))")
public void pointcutName(){}

// 声明切入的前置逻辑
@Before("pointcutName()")
public void before(JoinPoint joinPoint) {
// 可以通过JoinPoint获取到pointcut方法的详细信息
System.out.println("Before --> 获取到Joinpoint的方法名: " + joinPoint.getSignature().getName());
}

// 声明切入的后置逻辑
@After("pointcutName()")
public void after(JoinPoint joinPoint) {
// 可以通过JoinPoint获取到pointcut方法的详细信息
System.out.println("After --> 获取到Joinpoint的方法名: " + joinPoint.getSignature().getName());
}

// 声明切入的环绕逻辑(即在方法执行前后切入逻辑)
@Around("pointcutName()")
public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();

try {
// 调用执行链
return joinPoint.proceed();
} finally {
System.out.println(String.format("cost time %s", System.currentTimeMillis() - start));
}
}
}

// 配置类
@Configuration
// 启用AOP
@EnableAspectJAutoProxy
public class AspectConfigure {

/**
* 实现接口的目标对象
* @return
*/
@Bean
public Fly fly() {
return new FlyImpl();
}

/**
* 切面
* @return
*/
@Bean
public PerformanceTraceAspect performanceTraceAspect() {
return new PerformanceTraceAspect();
}

}

// 运行应用
public class AspectJDemo {
// 使用配置类,初始化容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfigure.class);
// 从容器中获取业务接口(此时已经是被处理过的代理对象,即已经切入了切面逻辑)
Fly fly = context.getBean(Fly.class);
fly.fly();
}

剖析demo运行机制

从@EnableAspectJAutoProxy注解开始解开神秘面纱

注解EnableAspectJAutoProxy的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

// 是否使用cglib来生成代理
boolean proxyTargetClass() default false;

// 是否将代理绑定到ThreadLocal,后续在目标类中可以使用AopContext.currentProxy()来获取代理对象
boolean exposeProxy() default false;

}
AspectJAutoProxyRegistrar配置类

重点在其元注解@Import上(有机会再分析一下关于spring的@Import注解导入机制),其导入了配置类AspectJAutoProxyRegistrar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

/**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 重点!!此处向容器注入了AnnotationAwareAspectJAutoProxyCreator类
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}

}

注册过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public abstract class AopConfigUtils {
/**
* Stores the auto proxy creator classes in escalation order.
*/
private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<Class<?>>();

/**
* Setup the escalation list.
* 在spring中,默认存在三个代理生成类。优先级别从上到下排序,越往后优先级越高
*/
static {
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); // 最高优先级
}

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
// 如果容器当前已经注册了代理生成器类,则比较其与AnnotationAwareAspectJAutoProxyCreator的优先级。取优先级最高的那个作为代理生成器注册在容器中。
// 显然AnnotationAwareAspectJAutoProxyCreator被注册到容器中
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
}
注册流程总结
  1. @EnableAspectJAutoProxy注解导入了配置类AspectJAutoProxyRegistrar
  2. 配置类AspectJAutoProxyRegistrar调用了AopConfigUtils来注册代理生成器AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator如何做到自动生成代理

类结构

主流程

源码分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName);

if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}

// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
if (beanName != null) {
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
this.targetSourcedBeans.add(beanName);
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
}

return null;
}

// 生成代理对象
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 跳过基础设施类
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 跳过基础设施类
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

// Create proxy if we have advice.
// 获取切面Advisor
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}

this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);

if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 解析目标对象接口
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}

// 生成Advisor
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}

// 生成代理
return proxyFactory.getProxy(getProxyClassLoader());
}

}

总结

  1. 描述了@AspectJ形式的Spring AOP如何使用
  2. spring AOP可以使用的@AspectJ标识符,如execution、within、this、target、@annotation等
  3. 描述了spring内部是如何使用@EnableAspectJAutoProxy注解来启用Spring AOP功能的,以及代理生成器AnnotationAwareAspectJAutoProxyCreator的内部流程时如何根据@Aspect类,来自动生成代理的