Spring Boot AOP 简介
什么是AOP?
AOP(面向切面编程)是一种软件开发范式,旨在通过在程序中插入称为“切面”的特殊代码,来提高代码的可维护性、可重用性和可扩展性。切面是与业务逻辑无关的代码,用于横切关注点,例如日志记录、性能监测、事务管理等。
在传统的面向对象编程中,应用程序的逻辑通常被分解为多个对象,这些对象协同工作以完成任务。而AOP允许你通过横切关注点的方式来封装这些协同工作,从而提高代码的模块化程度。
AOP中的关键概念
切面(Aspect)
切面是一个模块化单元,其中包含有关横切关注点的信息。它包含了切点、通知和一些其他配置。
切点(Pointcut)
切点是一个表达式,它定义了在哪里以及何时应该应用切面。它指定了在程序中的哪些位置切入,例如在方法执行之前或之后。
通知(Advice)
通知是在切入点上执行的代码。Spring框架定义了几种类型的通知,包括前置通知(在方法执行前执行)、后置通知(在方法执行后执行)、异常通知(在方法抛出异常时执行)、环绕通知(在方法执行前后都执行)等。
连接点(Join Point)
连接点是在应用程序执行期间能够插入切面的点。通常,连接点是方法的执行。
源码分析
我们要在SpringBoot项目中使用AOP功能,需要在我们的Pom.xml文件中引入以下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
那么会间接引入如下依赖的jar包
从截图中可以看到又间接依赖了Aspectj的jar包,那么我们的项目的Classpath中就存在了Advice这个类,这个点很关键一会会具体说到这个类带来的作用。
在SpringBoot的spring-boot-autoconfigurejar包中有一个自动配置类,它是org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,它的部分代码截图如下:
可以看出里面有个自动配置类是AspectJAutoProxyingConfiguration,它的条件注解是@ConditionalOnClass(Advice.class),即我们的ClassPath下面存在Advice这个类的时候才会被加载,前面已经说过了我们这个类路径下正好存在Advice这个类,那么就会加载AspectJAutoProxyingConfiguration,那么下面的静态自动配置类也会加载会解析CglibAutoProxyConfiguration这个配置类,进而解析上面的注解标签@EnableAspectJAutoProxy(proxyTargetClass = true),这个注解里面是什么内容那?先看看代码
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
//proxyTargetClass 用于决定是使用基于接口的代理还是基于类的代理,默认为 false 表示使用基于接口的代理。
boolean proxyTargetClass() default false;
//exposeProxy 用于在切面内部暴露代理对象,以便切面可以访问代理对象,通常用于解决循环依赖的问题。
boolean exposeProxy() default false;
}
这里面又引入了一个AspectJAutoProxyRegistrar对象,该对象实现了ImportBeanDefinitionRegistrar接口,我们重点看registerBeanDefinitions方法的定义:
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
首先调用AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,沿着方法的执行会执行到registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,该方法的代码如下:
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
可以看到这个方法又调用了registerOrEscalateApcAsRequired方法,传入了一个AnnotationAwareAspectJAutoProxyCreator类型的Class类,这个方法代码如下:
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
} else {
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", -2147483648);
beanDefinition.setRole(2);
registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
return beanDefinition;
}
}
判断容器注册类中是否包含一个Bean名称是org.springframework.aop.config.internalAutoProxyCreator的Bean定义,很显然第一次指定是不包含的,会走到else方法,创建一个类型是AnnotationAwareAspectJAutoProxyCreator的Bean定义对象RootBeanDefinition,添加一个order的属性,并且设置为最高优先级,然后把这个Bean定义对象放到容器注册中,bean的名字是org.springframework.aop.config.internalAutoProxyCreator。
回到registerBeanDefinitions方法的执行,这次判断注解EnableAspectJAutoProxy上的属性值的内容,判断proxyTargetClass的属性是否为true,如果为true说明是cglib代理,本例子中的确是用的cglib代理,然后调用AopConfigUtils.forceAutoProxyCreatorToUseClassProxying方法,该方法的代码如下:
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
该方法判断是否有org.springframework.aop.config.internalAutoProxyCreator的Bean定义,由于上面的方法已经注册进去了,所以会走到if语句中,又往Bean的定义中添加了一个proxyTargetClass的属性,值为true。
好了,到这为止Aop的加载过程分析就结束了,比较简单,下面我做一个总结。
总结
本节阐述了Aop的概念以及加载AOP的源码分析,总的来说就是我们项目中如果需要使用Aop的功能需要引入maven的依赖,spring-boot-starter-aop,然后项目自动会引入AspectJ的jar包依赖,由于springboot自动配置类AopAutoConfiguration中判断类路径下是否有Advice这个类,如果有会加载自动配置类CglibAutoProxyConfiguration进而又导入了AspectJAutoProxyRegistrar对象然后往spring容器中添加了一个Bean名称是org.springframework.aop.config.internalAutoProxyCreator,类型是AnnotationAwareAspectJAutoProxyCreator的类,下一篇文章我会继续分析AnnotationAwareAspectJAutoProxyCreator是什么以及调用过程都发生了什么事情。
好了,我是程序员老徐,如果喜欢我的文章请点赞关注,我们下期见。