Spring AOP基本原理
Spring AOP 是基于动态代理机制实现的,通过动态代理机制生成目标对象的代理对象,当外部调用目标对象的相关方法时,Spring注入的其实是代理对象Proxy,通过调用代理对象的方法执行AOP增强处理,然后回调目标对象的方法。
失效原因
AOP
使用的是JDK动态代理或者cglib代理的机制,它会给类生成一个代理类,事务的相关操作都在代理类上完成。
而当类内部调用增强方法时,使用的是实例调用,并没有通过代理类调用方法,所以会导致事务失效
只有引用的是被 动态代理
所创对象,才能被Spring增强,实现期望的AOP功能。
复现
创建了 Spring Boot
项目,依赖信息
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
定义注解和切面类
1 2 3 4 5
| @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { }
|
切面类
1 2 3 4 5 6 7 8 9 10 11
| @Aspect @Component public class LogAspect {
@Around("@annotation(com.example.demo.aop.Log)") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object result = proceedingJoinPoint.proceed(); System.out.println("进入切面方法"); return result; } }
|
内部调用
定义了两个方法 login()
和 status()
在 login()
方法上使用了自定义注解,同时在同一个类中的 status()
方法里调用了 login()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Component public class User {
@Log public void login() { System.out.println("用户已登录"); }
public void status() { System.out.println("用户登录状态"); this.login(); } }
|
写一个测试方法
1 2 3 4 5 6 7 8 9 10
| @SpringBootTest public class AopTest { @Resource private User user;
@Test public void test() { user.status(); } }
|
输出结果
可以发现增强方法失效
解决方法
通过 ApplicationContext 引入 bean 对象
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
| @Component public class User {
@Resource private ApplicationContext applicationContext;
private User getProxyClassObject() { return applicationContext.getBean(this.getClass()); }
@Log public void login() { System.out.println("用户已登录"); }
public void status() { User user = getProxyClassObject(); System.out.println("用户登录状态"); user.login(); } }
|
引入自身bean对象
不推荐,有循环依赖问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Component public class User {
@Resource
@Lazy private User user;
@Log public void login() { System.out.println("用户已登录"); }
public void status() { System.out.println("用户登录状态"); user.login(); } }
|
引入AopContext获取当前类的代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Component
@EnableAspectJAutoProxy(exposeProxy=true) public class User {
@Log public void login() { System.out.println("用户已登录"); }
public void status() { System.out.println("用户登录状态"); ((User)AopContext.currentProxy()).login(); } }
|