笔记参考B站黑马程序员视频:spring原理
1.spring代理选择
AOP(面向切面编程)中的两个重要概念:切面(aspect)和切点(pointcut)。
- 切面(Aspect):切面是横切关注点的模块化,它包含了通知(advice)和切点(pointcut)的组合。在AOP中,切面可以看作是一种将特定行为横向切割出来的模块,用于对系统的多个部分进行横切式的功能增强。一个切面可以包含多个通知和对应的切点。
- 切点(Pointcut):切点定义了在哪些连接点上应用通知。通常,切点使用表达式来描述哪些方法将被拦截并应用通知。切点实际上是一个条件,它决定了通知应该在哪里执行。
- 通知(Advice):通知是切面的具体行为(具体方法),它定义了在连接点上执行的操作。常见的通知类型包括前置通知(Before advice)、后置通知(After advice)、环绕通知(Around advice)、异常通知(After throwing advice)等。
- Advisor:Advisor是Spring AOP框架中的一个概念,用于描述细粒度的切面。一个Advisor包含一个通知(Advice)和一个切点(Pointcut)。Advisor用于提供更细粒度的控制,可以选择性地在特定的切点上应用通知。
切点Pointcut


实现类

使用的示例代码:
加入依赖:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>全部代码如下:
package com.dreams.demo14;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
public class Demo {
public static void main(String[] args) {
// 1. 备好切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
// 2. 备好通知
MethodInterceptor advice = invocation -> {
System.out.println("before...");
Object result = invocation.proceed(); // 调用目标
System.out.println("after...");
return result;
};
// 3. 备好切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
Target2 target = new Target2();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
factory.setInterfaces(target.getClass().getInterfaces());
factory.setProxyTargetClass(false);
Target2 proxy = (Target2) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
}
interface I1 {
void foo();
void bar();
}
static class Target1 implements I1 {
public void foo() {
System.out.println("target1 foo");
}
public void bar() {
System.out.println("target1 bar");
}
}
static class Target2 {
public void foo() {
System.out.println("target2 foo");
}
public void bar() {
System.out.println("target2 bar");
}
}
}运行结果:

代码解释:
AspectJExpressionPointcut 是 Spring Framework 中用于定义切点表达式的类。它允许开发人员使用 AspectJ 风格的表达式来描述在哪些连接点上应该应用通知。在 Spring AOP 中,切点表达式可以用于配置切面,以指定在哪些方法或类上应用通知。
// 1. 备好切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
使用MethodInterceptor (注意不是CGLIB的同名)需要实现 invoke 方法,该方法接收一个 MethodInvocation 对象作为参数,然后可以在目标方法被调用之前或之后执行额外的逻辑。
// 2. 备好通知
MethodInterceptor advice = invocation -> {
System.out.println("before...");
Object result = invocation.proceed(); // 调用目标
System.out.println("after...");
return result;
};
DefaultPointcutAdvisor 是 Spring Framework 中用于定义切面(Aspect)的一个常用类。它实现了 Advisor 接口,用于将特定的通知(Advice)应用于特定的切点(Pointcut)。
// 3. 备好切面 DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
ProxyFactory factory = new ProxyFactory()创建了一个 ProxyFactory 对象,用于创建代理对象,这个代理对象可以拦截并增强目标对象的方法调用,以实现横切关注点(cross-cutting concerns)的功能。。factory.setTarget(target); 设置目标对象为 factory 的目标。factory.addAdvisor(advisor); 向 factory 中添加了一个切面通知器(Advisor),这个通知器包括了切点和通知。factory.setInterfaces(target.getClass().getInterfaces()); 设置代理对象实现的接口,这里是使用目标对象的接口作为代理对象的接口,这里只有设置进去ProxyTargetClass属性才能生效。factory.setProxyTargetClass(false); 设置是否使用 CGLIB 动态代理,默认为 false,表示不强制使用 CGLIB 动态代理。
Target2 target = new Target2(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target); factory.addAdvisor(advisor); factory.setInterfaces(target.getClass().getInterfaces()); factory.setProxyTargetClass(false); Target2 proxy = (Target2) factory.getProxy(); System.out.println(proxy.getClass()); proxy.foo(); proxy.bar();
上面的代码底层选择使用JDK动态代理或CGLIB代理,关键是proxyTargetClass属性
proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
proxyTargetClass = true, 总是使用 cglib 实现
2.切点匹配
匹配使用matches 方法,作用是判断某个方法是否匹配指定的切点表达式。
package com.dreams.demo16;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.transaction.annotation.Transactional;
public class Demo {
public static void main(String[] args) throws NoSuchMethodException {
AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
pt1.setExpression("execution(* bar())");
System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));
AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));
}
static class T1 {
@Transactional
public void foo() {
}
public void bar() {
}
}
@Transactional
static class T2 {
public void foo() {
}
}
@Transactional
interface I3 {
void foo();
}
static class T3 implements I3 {
public void foo() {
}
}
}pt2.setExpression(“@annotation(org.springframework.transaction.annotation.Transactional)”); 设置了切点表达式,这里使用了 AspectJ 的注解方式来定义切点,表示匹配带有 @Transactional 注解的方法。pt2.matches(T1.class.getMethod(“foo”), T1.class) 调用 matches 方法,传入要匹配的方法对象以及目标类对象,判断指定的方法是否匹配切点。
matches 方法用于判断某个方法是否匹配指定的切点表达式。
运行

所以spring底层使用另一种方法
public static void main(String[] args) throws NoSuchMethodException {
StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 检查方法上是否加了 Transactional 注解
MergedAnnotations annotations = MergedAnnotations.from(method);
if (annotations.isPresent(Transactional.class)) {
return true;
}
// 查看类上是否加了 Transactional 注解
annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
if (annotations.isPresent(Transactional.class)) {
return true;
}
return false;
}
};
System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));
}方法首先检查传入的方法对象 (method) 是否带有 @Transactional 注解。使用 MergedAnnotations.from(method) 获取方法上的注解并检查是否存在 @Transactional 注解。如果方法上没有注解,再检查类级别的注解。使用 MergedAnnotations.from(targetClass,MergedAnnotations.SearchStrategy.TYPE_HIERARCHY) 获取类及其层次结构上的注解,并检查是否存在 @Transactional 注解。

3.@Aspect与Advisor
定义一个高级切面和低级切面
package com.dreams.autoproxy;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;
public class Demo {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
static class Target1 {
public void foo() {
System.out.println("target1 foo");
}
}
static class Target2 {
public void bar() {
System.out.println("target2 bar");
}
}
@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
}
@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}
}运行如下:

要想处理上面的切面,就得加入专门的Bean处理器
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);

AnnotationAwareAspectJAutoProxyCreator 的主要责任是在 Spring 容器启动时,检测带有 @Aspect 注解的类,并为它们创建动态代理。这样一来,被 @Aspect 注解的类就可以作为切面类来实现 AOP 编程。
findEligibleAdvisors 方法用于查找符合条件的 Advisor。找的是低级切面,如果是高级切面需要转换成低级切面。

wrapIfNecessary判断是否需要代理,检查给定的 Bean 是否符合代理的条件,例如是否有匹配的 Advisor 或者切面。如果需要代理,则创建相应的代理对象。

不过这些方法都是受保护的,所以可以取同名包下来创建(java中不行,都是spring可以)。
复制上面的代码,放到同名包下
package org.springframework.aop.framework.autoproxy;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import java.util.List;
public class A17 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
context.refresh();
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2");
for (Advisor advisor : advisors) {
System.out.println(advisor);
}
}
static class Target1 {
public void foo() {
System.out.println("target1 foo");
}
}
static class Target2 {
public void bar() {
System.out.println("target2 bar");
}
}
@Aspect // 高级切面类
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
}
@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}
}我们测试一下findEligibleAdvisors 方法,返回与之对应的advisors
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2");
for (Advisor advisor : advisors) {
System.out.println(advisor);
}Target2没有符合切面管理,所以没有输出

换成Target1

输出了4个
org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR是spring给所有切面都要加的,它会将 MethodInvocation 放入当前线程的上下文中。这使得在方法调用链中的其他地方(例如,在业务逻辑代码中)可以访问当前的 MethodInvocation,从而获得有关调用的详细信息。
org.springframework.aop.support.DefaultPointcutAdvisor就是我们上面定义的低级切面
剩下的两个InstantiationModelAwarePointcutAdvisor就是我们定义的高级切面转换后的低级切面
wrapIfNecessary 方法首先检查当前 Bean 是否应该跳过代理(可能基于某些配置或条件)。查找符合条件的 Advisor,内部会使用 findEligibleAdvisors 方法查找与当前 Bean 匹配的 Advisor。根据找到的 Advisor 和其他条件来判断是否需要为当前 Bean 创建代理。
如果需要代理,则实际创建代理对象(通常通过 JDK 动态代理或 CGLIB 代理)。如果创建了代理对象,则返回该代理对象;否则,返回原始的 Bean。
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class); Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1"); System.out.println(o1.getClass()); Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2"); System.out.println(o2.getClass()); ((Target1) o1).foo();

高级切面与低级切面的执行顺序
@Aspect // 高级切面类
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
@After("execution(* foo())")
public void after() {
System.out.println("aspect1 after...");
}
}
@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}默认顺序

如果给高级切面加上@Order(1)
@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
@After("execution(* foo())")
public void after() {
System.out.println("aspect1 after...");
}
}
如果低级切面想要设置,就不能加注解,而是要直接代码配置。
@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
advisor.setOrder(2);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}
4.代理对象创建时机
看如下代码:
package org.springframework.aop.framework.autoproxy;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import javax.annotation.PostConstruct;
public class Demo {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(Config.class);
context.refresh();
context.close();
// 创建 -> (*) 依赖注入 -> 初始化 (*)
}
@Configuration
static class Config {
@Bean // 解析 @Aspect、产生代理
public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}
@Bean // 解析 @Autowired
public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
return new AutowiredAnnotationBeanPostProcessor();
}
@Bean // 解析 @PostConstruct
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
return new CommonAnnotationBeanPostProcessor();
}
@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice);
}
@Bean
public MethodInterceptor advice() {
return (MethodInvocation invocation) -> {
System.out.println("before...");
return invocation.proceed();
};
}
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {
public void foo() {
}
public Bean1() {
System.out.println("Bean1()");
}
@PostConstruct
public void init() {
System.out.println("Bean1 init()");
}
}
static class Bean2 {
public Bean2() {
System.out.println("Bean2()");
}
@Autowired
public void setBean1(Bean1 bean1) {
System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
}
@PostConstruct
public void init() {
System.out.println("Bean2 init()");
}
}
}Bean1 类有一个 foo() 方法和一个 @PostConstruct 注解的 init() 方法。
Bean2 类在构造函数中输出,同时通过 @Autowired 注解注入 Bean1 实例。 这种情况也就是无循环依赖时,代理的创建时机是初始化(@PostConstruct注init方法)之后。如下图:

如果Bean1同样注入Bean2,这时就是有循环依赖时
static class Bean1 {
public void foo() {
}
public Bean1() {
System.out.println("Bean1()");
}
@Autowired
public void setBean2(Bean2 bean2) {
System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
}
@PostConstruct
public void init() {
System.out.println("Bean1 init()");
}
}如下图:
这里的顺序是:
- Bean1() 和 Bean2() 的构造函数被调用。Bean1() 和 Bean2() 的初始化是在Bean实例化时发生的,这时还没有完成依赖注入。
- Spring 使用了AOP(面向切面编程)来创建代理,这是由于使用了Spring的自动代理功能,如 AnnotationAwareAspectJAutoProxyCreator 提示的那样。这里创建Bean1的代理。
- Bean2 的 setBean1(bean1) 方法被调用,传入的参数是 Bean1 的代理对象,即 Demo$Bean1$$EnhancerBySpringCGLIB$$b5922e5b。
这里涉及到Spring通过CGLIB动态代理技术为 Bean1 生成的代理对象。 - Bean2 的初始化方法 init() 被调用。
init() 方法是在 Bean2 的所有依赖注入完成后调用的初始化方法。 - Bean1 的 setBean2(bean2) 方法被调用,传入的参数是 Bean2 的原始实例。
- Bean1 的初始化方法 init() 被调用。init() 方法是在 Bean1 的所有依赖注入完成后调用的初始化方法。
如下图:
创建代理对象在初始化(@PostConstruct注init方法)之前了

5.高级切面转换低级切面
高级切面转换低级切面示例代码
package org.springframework.aop.framework.autoproxy;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectInstanceFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class Test {
static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}
}
static class Target {
public void foo() {
System.out.println("target foo");
}
}
public static void main(String[] args) throws Throwable {
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}
}
}通过反射获取 Aspect 类中的所有方法,并检查哪些方法标有 @Before 注解。对于每个标有 @Before 注解的方法:获取注解中的切点表达式。创建一个 AspectJExpressionPointcut,并设置其表达式。创建一个 AspectJMethodBeforeAdvice,它将通知方法、切点和实例工厂关联起来。创建一个 DefaultPointcutAdvisor,将切点和通知关联起来。将创建的 Advisor 添加到列表中。

@Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式,该对象包含了如下信息
- a. 通知代码从哪儿来
- b. 切点是什么(这里为啥要切点, 后面解释)
- c. 通知对象如何创建, 本例共用同一个 Aspect 对象
如果是其他通知,转换为类似的形式:
- AspectJAroundAdvice (环绕通知)
- AspectJAfterReturningAdvice
- AspectJAfterThrowingAdvice
- AspectJAfterAdvice
6.静态通知调用
不同通知统一转换为环绕通知
其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象:
- 因为 advisor 有多个,且一个套一个调用, 因此需要一个调用链对象,即 MethodInvocation
- MethodInvocation 要知道 advice 有哪些,还要知道目标
如图:

从上图看出, 环绕通知才适合作为 advice,因此其他 before、afterReturning 都会被转换成环绕通知,统一转换为环绕通知,体现的是设计模式中的适配器模式,对外是为了方便使用要区分 before、afterReturning,对内统一都是环绕通知,统一用 MethodInterceptor 表示。

示例代码:
package org.springframework.aop.framework;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.*;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class Demo {
static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}
public void after() {
System.out.println("after");
}
@AfterReturning("execution(* foo())")
public void afterReturning() {
System.out.println("afterReturning");
}
@AfterThrowing("execution(* foo())")
public void afterThrowing(Exception e) {
System.out.println("afterThrowing " + e.getMessage());
}
@Around("execution(* foo())")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
}
static class Target {
public void foo() {
System.out.println("target foo");
}
}
@SuppressWarnings("all")
public static void main(String[] args) throws Throwable {
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 1. 高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(AfterReturning.class)) {
// 解析切点
String expression = method.getAnnotation(AfterReturning.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(Around.class)) {
// 解析切点
String expression = method.getAnnotation(Around.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}
}
}先简单输出,可以看到对应的四个对象

通知统一转换为环绕通知 MethodInterceptor
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);是spring给所有切面都要加的,它会将 MethodInvocation 放入当前线程的上下文中。这使得在方法调用链中的其他地方(例如,在业务逻辑代码中)可以访问当前的 MethodInvocation,从而获得有关调用的详细信息
Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程
proxyFactory.addAdvisors(list);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
for (Object o : methodInterceptorList) {
System.out.println(o);
}下面转换的4个对象都是实现了 MethodInterceptor

此步获取所有执行时需要的 advice (静态)即统一转换为 MethodInterceptor 环绕通知, 这体现在方法名中的 Interceptors 上
适配如下:
- MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
- AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
例如:

创建并执行调用链 (环绕通知s + 目标)
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 3. 创建并执行调用链 (环绕通知s + 目标)
MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
);
// 执行
methodInvocation.proceed();
7.动态通知调用
动态通知调用,需要参数绑定,执行时还需要切点对象,复杂程度高,性能比无参数绑定的通知调用低
对应的对象如下:
InterceptorAndDynamicMethodMatcher 里有一个切点对象和通知对象

package org.springframework.aop.framework.autoproxy;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import java.lang.reflect.Field;
import java.util.List;
public class A19 {
@Aspect
static class MyAspect {
@Before("execution(* foo(..))") // 静态通知调用,不带参数绑定,执行时不需要切点
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo(..)) && args(x)") // 动态通知调用,需要参数绑定,执行时还需要切点对象
public void before2(int x) {
System.out.printf("before2(%d)%n", x);
}
}
static class Target {
public void foo(int x) {
System.out.printf("target foo(%d)%n", x);
}
}
@Configuration
static class MyConfig {
@Bean
AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
}
public static void main(String[] args) throws Throwable {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(MyConfig.class);
context.refresh();
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> list = creator.findEligibleAdvisors(Target.class, "target");
Target target = new Target();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisors(list);
Object proxy = factory.getProxy(); // 获取代理
List<Object> interceptorList = factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo", int.class), Target.class);
for (Object o : interceptorList) {
showDetail(o);
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(
proxy, target, Target.class.getMethod("foo", int.class), new Object[]{100}, Target.class, interceptorList
) {};
invocation.proceed();
}
public static void showDetail(Object o) {
try {
Class<?> clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");
if (clazz.isInstance(o)) {
Field methodMatcher = clazz.getDeclaredField("methodMatcher");
methodMatcher.setAccessible(true);
Field methodInterceptor = clazz.getDeclaredField("interceptor");
methodInterceptor.setAccessible(true);
System.out.println("环绕通知和切点:" + o);
System.out.println("\t切点为:" + methodMatcher.get(o));
System.out.println("\t通知为:" + methodInterceptor.get(o));
} else {
System.out.println("普通环绕通知:" + o);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
8.参考
黑马程序员:spring


