Spring AOP实现原理解析

文章目录

  • 1. 前言
  • 2. 代理示例
  • 3. 问题分析
  • 4. 结尾

1. 前言

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

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

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

Spring AOP实现原理解析_第1张图片

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的调用时机

Spring AOP实现原理解析_第2张图片


问题2:上一步提的处理器AnnotationAwareAspectJAutoProxyCreator没有加@Component注解,为什么能被Spring扫描到并起作用?
Spring AOP实现原理解析_第3张图片
答案2:----------------------------------------------------------------------------------------

  • Spring AOP生效必须加注解@EnableAspectJAutoProxy,该注解使用@Import将AspectJAutoProxyRegistrar加入了Spring容器
    Spring AOP实现原理解析_第4张图片

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

  • AspectJAutoProxyRegistrar#registerBeanDefinitions() 将 AnnotationAwareAspectJAutoProxyCreator 加入了 Spring 容器
    Spring AOP实现原理解析_第5张图片

问题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基本原理就介绍到这里了,后面有新的想法会不断补充,也欢迎大家提出新的见解。

你可能感兴趣的:(Spring,Boot,java,spring,aop)