Spring AOP实现原理解析

1. 前言

AOP,英文全称是Aspect Oriented Programming,也叫作面向切面编程。预先定义一个或多个切入点,当程序执行到切点的方法时,会先执行切面相关处理逻辑,再执行原程序代码。

注:本篇文章会结合Spring生命周期源码,介绍AOP是如何整合到Sping容器管理。不会过多地介绍一些基础知识,阅读之前,最好对AOP、CGLIB、Proxy有个基础的了解。

Spring通过动态代理实现AOP,用语言表述可能不大清楚,下面画一张图来对比一下

在这里插入图片描述

2. 代理示例

  • 1.创建Service接口:
public interface MyService {
	void test();
}
  • 2.创建ServiceImpl实现类,记得加 @Service 注解,表示由Spring容器管理:
@Service
public class MyServiceImpl implements MyService {

	@Autowired
	private A a;

	@Override
	public void test() {
		System.out.println("调用MyService.test");;
	}

	@PostConstruct
	public void init(){
		System.out.println("MyServiceImpl PostConstruct");
	}
}
  • 3.创建一个@Component标记的常规类:
@Component
public class A {

	public void test(){
		System.out.println("a.test...");
	}
}
  • 4.创建启动类,注意看这时候没有加注解 @EnableAspectJAutoProxy ,因此Spring不会启用AOP功能:
@ComponentScan("com.example")
public class App {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(App.class);

		A a = ac.getBean(A.class);
		a.test();
		System.out.println(a.getClass());

		System.out.println("------------------------------------------");

		MyService service = ac.getBean(MyService.class);
		service.test();
		System.out.println(service.getClass());
	}
}
  • 5.执行程序,输出结果如下。此时,所有的类都是普通的JAVA对象:
MyServiceImpl PostConstruct
a.test...
class com.example.demo.A
------------------------------------------
调用MyService.test
class com.example.service.impl.MyServiceImpl
  • 6.接下来,准备实现AOP了。在前面的基础上,创建“切面类” TestAspect:
@Component
@Aspect
public class TestAspect {

	//这里通过通配符,表示之前的A类和MyServiceImpl类都会被代理。
	//具体的@Pointcut配置可以查看官网https://docs.spring.io/spring-framework/docs/5.2.13.RELEASE/spring-framework-reference/core.html#spring-core
	@Pointcut("execution(* com.example.*.*.*(..))")
	public void myPointCut(){}

	@Before("myPointCut()")
	public void before(){
		System.out.println("before");
	}
}
  • 7.在启动类增加 @EnableAspectJAutoProxy 注解,然后重新运行程序,新的执行结果如下。此时,A对象变成CGLIB创建的动态代理对象,而service变成JDK创建的动态代理对象:
MyServiceImpl PostConstruct
before
a.test...
class com.example.demo.A$$EnhancerBySpringCGLIB$$7c975a0a
------------------------------------------
before
调用MyService.test
class com.sun.proxy.$Proxy23

3. 问题分析

问题1:Spring在什么时机点进行AOP处理?

答案1:

  • 在创建Bean对象之后,调用后置处理器方法AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization()创建动态代理实现

看下面这张图,描述了Bean创建的过程和AOP的调用时机

在这里插入图片描述


问题2:上一步提的处理器AnnotationAwareAspectJAutoProxyCreator没有加@Component注解,为什么能被Spring扫描到并起作用?
在这里插入图片描述
答案2:----------------------------------------------------------------------------------------

  • Spring AOP生效必须加注解@EnableAspectJAutoProxy,该注解使用@Import将AspectJAutoProxyRegistrar加入了Spring容器
    在这里插入图片描述

  • AspectJAutoProxyRegistrar是实现ImportBeanDefinitionRegistrar接口的处理器,在Spring扫描类的过程中,会调用所有实现类的 registerBeanDefinitions 方法
    在这里插入图片描述

  • AspectJAutoProxyRegistrar#registerBeanDefinitions() 将 AnnotationAwareAspectJAutoProxyCreator 加入了 Spring 容器
    在这里插入图片描述

问题3:Spring采用哪种动态代理机制,CGLIB还是JDK?

答案3:----------------------------------------------------------------------------------------

  • 默认情况下,实现了业务接口的Bean会采用JDK动态代理,例如:ServiceImpl。其他情况下,一般会采用CGLIB动态代理。
  • 设置注解 @EnableAspectJAutoProxy 的属性 proxyTargetClass = true,会强制 CGLIB 动态代理

修改之前的例子,使用注解 @EnableAspectJAutoProxy(proxyTargetClass = true) ,重新运行程序,执行结果如下:

MyServiceImpl PostConstruct
before
a.test...
class com.example.demo.A$$EnhancerBySpringCGLIB$$7c975a0a
------------------------------------------
before
调用MyService.test
class com.example.service.impl.MyServiceImpl$$EnhancerBySpringCGLIB$$98d88524

看吧… 全部变成CGLIB创建的代理对象

4. 结尾

本篇的AOP基本原理就介绍到这里了,后面有新的想法会不断补充,也欢迎大家提出新的见解。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

°Fuhb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值