10. Spring AOP源码解析
Posted IT BOY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了10. Spring AOP源码解析相关的知识,希望对你有一定的参考价值。
10 Spring AOP源码解析
目录
Pt1 Spring AOP基础知识
AOP是Aspect Oriented Programming的缩写,意思是面向切面编程,通过预编译和运行时的动态代理,实现在不修改源代码的情况下给程序动态添加功能。同时AOP也为服务方和调用方实现解耦提供了很有效的方式。
在日常编码中,通常分为业务代码和非业务代码,比如事务、日志等,这些代码和业务代码混在一起。举个最常见的例子,就是JDBC的事务处理,有大量的模板代码是的事务处理代码,这些大量重复的、复制粘贴的、和业务处理无关的代码,为维护带来了风险和困难。AOP通过代理机制,可以将这些非业务代码剥离,使得他们可以独立于业务代码存在,从而降低维护的难度和风险。
所以,Spring AOP是一种编程范式,主要目的是将非功能性需求从功能型需求中分离出来,达到解耦的目标。
Pt1.1 AOP核心概念
-
Aspect(切面)
Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
-
Jointpoint(连接点)
表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
-
Pointcut(切点)
表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
-
Advice(增强)
Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
-
@Before:该注解标注的方法在业务模块代码执行之前执行,其不能阻止业务模块的执行,除非抛出异常;
-
@AfterReturning:该注解标注的方法在业务模块代码执行之后执(正常返回)行;
-
@AfterThrowing:该注解标注的方法在业务模块抛出指定异常后执行;
-
@After:该注解标注的方法在所有的Advice执行完成后执行,无论业务模块是否抛出异常,类似于finally的作用;
-
@Around:该注解功能最为强大,其所标注的方法用于编写包裹业务模块执行的代码,其可以传入一个ProceedingJoinPoint用于调用业务模块的代码,无论是调用前逻辑还是调用后逻辑,都可以在该方法中编写,甚至其可以根据一定的条件而阻断业务模块的调用;
-
@DeclareParents:其是一种Introduction类型的模型,在属性声明上使用,主要用于为指定的业务模块添加新的接口和相应的实现。
-
@Aspect:严格来说,其不属于一种Advice,该注解主要用在类声明上,指明当前类是一个组织了切面逻辑的类,并且该注解中可以指定当前类是何种实例化方式,主要有三种:singleton、perthis和pertarget,具体的使用方式后面会进行讲解。
-
-
Target(目标对象)
织入 Advice 的目标对象.。目标对象可以被一个或者多个切面织入。
-
Weaving(织入)
将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程,就是执行method同时执行advice。
-
AOP代理(AopProxy)
在Spring中有两种形式的代理:JDK动态代理和CGLib代理。后面会详细介绍。
Pt1.2 AOP切点配置规则
(1) execution
由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。
如下是execution表达式的语法:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下:
-
modifiers-pattern:方法的可见性,如public,protected;
-
ret-type-pattern:方法的返回值类型,如int,void等;
-
declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
-
name-pattern:方法名类型,如buisinessService();
-
param-pattern:方法的参数类型,如java.lang.String;
-
throws-pattern:方法抛出的异常类型,如java.lang.Exception;
如下是一个使用execution表达式的例子:
execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))
上述切点表达式将会匹配使用public修饰,返回值为任意类型,并且是com.spring.BusinessObject类中名称为businessService的方法,方法可以有多个参数,但是第一个参数必须是java.lang.String类型的方法。上述示例中我们使用了..通配符,关于通配符的类型,主要有两种:
-
*通配符,该通配符主要用于匹配单个单词,或者是以某个词为前缀或后缀的单词。
如下示例表示返回值为任意类型,在com.spring.service.BusinessObject类中,并且参数个数为零的方法:
execution(* com.spring.service.BusinessObject.*())
下述示例表示返回值为任意类型,在com.spring.service包中,以Business为前缀的类,并且是类中参数个数为零方法:
execution(* com.spring.service.Business*.*())
-
..通配符,该通配符表示0个或多个项,主要用于declaring-type-pattern和param-pattern中,如果用于declaring-type-pattern中,则表示匹配当前包及其子包,如果用于param-pattern中,则表示匹配0个或多个参数。
如下示例表示匹配返回值为任意类型,并且是com.spring.service包及其子包下的任意类的名称为businessService的方法,而且该方法不能有任何参数:
execution(* com.spring.service..*.businessService())
这里需要说明的是,包路径service...businessService()中的..应该理解为延续前面的service路径,表示到service路径为止,或者继续延续service路径,从而包括其子包路径;后面的.businessService(),这里的*表示匹配一个单词,因为是在方法名前,因而表示匹配任意的类。
如下示例是使用..表示任意个数的参数的示例,需要注意,表示参数的时候可以在括号中事先指定某些类型的参数,而其余的参数则由..进行匹配:
execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))
(2) within
within表达式的粒度为类,其参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕。如下是within表达式的语法:
within(declaring-type-pattern)
within表达式只能指定到类级别,如下示例表示匹配com.spring.service.BusinessObject中的所有方法:
within(com.spring.service.BusinessObject)
within表达式路径和类名都可以使用通配符进行匹配,比如如下表达式将匹配com.spring.service包下的所有类,不包括子包中的类:
within(com.spring.service.*)
如下表达式表示匹配com.spring.service包及子包下的所有类:
within(com.spring.service..*)
(3) args
args表达式的作用是匹配指定参数类型和指定参数数量的方法,无论其类路径或者是方法名是什么。这里需要注意的是,args指定的参数必须是全路径的。如下是args表达式的语法:
args(param-pattern)
如下示例表示匹配所有只有一个参数,并且参数类型是java.lang.String类型的方法:
args(java.lang.String)
也可以使用通配符,但这里通配符只能使用..,而不能使用*。如下是使用通配符的实例,该切点表达式将匹配第一个参数为java.lang.String,最后一个参数为java.lang.Integer,并且中间可以有任意个数和类型参数的方法:
args(java.lang.String,..,java.lang.Integer)
Pt2 Spring AOP使用示例
Pt2.1 AOP日志(XML)
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
-
spring.xml配置AOP信息
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 自动扫描装配 -->
<context:component-scan base-package="com.example.springdemo"/>
<!--启用spring的一些annotation -->
<context:annotation-config/>
<!-- AOP配置 -->
<bean id="logAspect" class="com.example.springdemo.aop.log.LogAspect"></bean>
<aop:config>
<aop:aspect id="log" ref="logAspect">
<aop:pointcut id="logPointcut" expression="execution(* com.example.springdemo.controller.LogController.LogDemo(..))"/>
<aop:before method="logBefore" pointcut-ref="logPointcut"/>
<aop:after method="logAfter" pointcut-ref="logPointcut"/>
</aop:aspect>
</aop:config>
</beans>
-
加载spring.xml
@Configuration
@ImportResource(locations = {"classpath:spring.xml"})
public class ConfigClass {
}
-
定义切面
public class LogAspect {
public LogAspect() {
System.out.println("init");
}
public void logBefore() {
System.out.println("Before execution log.");
}
public void logAfter() {
System.out.println("After execution log.");
}
}
-
定义Controller
@RestController
public class LogController {
@RequestMapping(value = "logAop", method = RequestMethod.GET)
public String LogDemo() {
return "Execute Log Aop test.";
}
}
-
运行Controller
Before execution log.
After execution log.
Pt2.2 JDBC(Annotation)
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
-
AOP配置
@Component
@Aspect
public class JdbcAspect {
// 定义切点
@Pointcut("execution(* com.example.springdemo.controller.JdbcController.jdbcDemo(..))")
public void jdbcPointcut() {
}
@Before("JdbcAspect.jdbcPointcut()")
public void jdbcBefore() {
System.out.println("begin transaction");
}
@AfterReturning("JdbcAspect.jdbcPointcut()")
public void jdbcAfter() {
System.out.println("commit transaction");
}
@AfterThrowing("JdbcAspect.jdbcPointcut()")
public void jdbcException() {
System.out.println("rollback transaction");
}
}
-
Controller
@RestController
public class JdbcController {
@RequestMapping(value = "jdbcAop", method = RequestMethod.GET)
public String jdbcDemo() {
return "Execute jdbc Aop test.";
}
}
-
测试请求
begin transaction
commit transaction
Pt3 Spring AOP核心类
Pt3.1 BeanPostProcessor
BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口,它可以监听容器触发的Bean生命周期事件。向容器中注册BeanPostProcessor之后,容器中管理的Bean就具备了接受IoC容器回调事件的能力。
源码如下:
/**
* BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口,它可以监听容器触发的Bean生命周期事件。
* 向容器中注册BeanPostProcessor之后,容器中管理的Bean就具备了接受IoC容器回调事件的能力。
*
* 他提供的两个回调方法和容器管理的Bean生命周期事件密切相关,可以为用户提供在Spring IoC容器初始化Bean过程中,去自定义的处理操作提供钩子事件。
*/
public interface BeanPostProcessor {
// 在Bean的初始化之前提供回调入口
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 在Bean的初始化之后提供回调入口
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
Pt3.2 TargetSource
在Spring代理目标bean的时候,其并不是直接创建一个目标bean的对象实例的,而是通过一个TargetSource类型的对象将目标bean进行封装,Spring Aop获取目标对象始终是通过TargetSource.getTarget()方法进行的。
换句话说,proxy(代理对象)代理的不是target,而是TargetSource。
通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如: target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。
源码实现:
/**
* 在Spring代理目标bean的时候,其并不是直接创建一个目标bean的对象实例的,而是通过一个TargetSource类型的对象将目标bean进行封装,
* Spring Aop获取目标对象始终是通过TargetSource.getTarget()方法进行的。
* 换句话说,proxy(代理对象)代理的不是target,而是TargetSource。
*
* 通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,
* 可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。
* 这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。
*/
public interface TargetSource extends TargetClassAware {
// 本方法主要用于返回目标bean的Class类型
@Override
@Nullable
Class<?> getTargetClass();
// 这个方法用户返回当前bean是否为静态的,比如常见的单例bean就是静态的,而prototype就是动态的,
// 这里这个方法的主要作用是,对于静态的bean,spring是会对其进行缓存的,在多次使用TargetSource
// 获取目标bean对象的时候,其获取的总是同一个对象,通过这种方式提高效率
boolean isStatic();
// 获取目标bean对象,这里可以根据业务需要进行自行定制
@Nullable
Object getTarget() throws Exception;
// Spring在用完目标bean之后会调用这个方法释放目标bean对象,对于一些需要池化的对象,这个方法是必须
// 要实现的,这个方法默认不进行任何处理
void releaseTarget(Object target) throws Exception;
}
Pt3.3 Advice
Advice表示的是在Pointcut切点上应该执行的方法,即切面Aspect中定义的方法逻辑。
这些方法可以在目标方法之前、之后、包裹、抛出异常等等任何地方执行。
源码:
// 标记通知方法
public interface Advice {
}
Pt3.4 Advisor
Advisor是 Pointcut 与 Advice 的组合,Advice 是执行的通知方法,而要知道方法何时执行,则 Advice 必需与 Pointcut 组合在一起,这就诞生了 Advisor 。
-
Advice表示通知的内容
-
Pointcut表示通知的时机
-
Advisor表示通知人(它知道通知的时机,且知道是通知的内容)
下图是Advisor的类图:
Advisor是顶层抽象接口,定义了操作Advice的行为:
// Pointcut 与 Advice 的组合
public interface Advisor {
// 空的Advice通知
Advice EMPTY_ADVICE = new Advice() {};
// 获取通知方法
Advice getAdvice();
// 判定当前Advice是否和特定目标值相关
boolean isPerInstance();
}
PointcutAdvisor继承Advisor,定义了操作Pointcut行为,此时具体了可以操作Pointcut和Advice的行为:
public interface PointcutAdvisor extends Advisor {
// 获取Advisor关联的Pointcut
Pointcut getPointcut();
}
AbstractPointcutAdvisor新增了排序和序列化的能力:
// AbstractPointcutAdvisor是在PointcutAdvisor的基础上,增加Ordered和Serializable接口的能力。
// Spring就提供了Ordered这个接口,来处理相同接口实现类的优先级问题。
@SuppressWarnings("serial")
public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordered, Serializable {
// order用于标记执行的优先级。
@Nullable
private Integer order;
// 调用者可以手动来指定Order
public void setOrder(int order) {
this.order = order;
}
// 获取执行顺序
@Override
public int getOrder() {
if (this.order != null) {
return this.order;
}
// 若调用者没有指定Order,那就拿advice的order为准(若有),否则LOWEST_PRECEDENCE表示最后执行
Advice advice = getAdvice();
if (advice instanceof Ordered) {
return ((Ordered) advice).getOrder();
}
return Ordered.LOWEST_PRECEDENCE;
}
// 该属性还没有被Spring使用,永远返回TURE
@Override
public boolean isPerInstance() {
return true;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PointcutAdvisor)) {
return false;
}
PointcutAdvisor otherAdvisor = (PointcutAdvisor) other;
return (ObjectUtils.nullSafeEquals(getAdvice(), otherAdvisor.getAdvice()) &&
ObjectUtils.nullSafeEquals(getPointcut(), otherAdvisor.getPointcut()));
}
@Override
public int hashCode() {
return PointcutAdvisor.class.hashCode();
}
}
AbstractGenericPointcutAdvisor保存Advice对象:
public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdvisor {
// 保存Advisor关联的Advice
private Advice advice = EMPTY_ADVICE;
// setter and getter
public void setAdvice(Advice advice) {
this.advice = advice;
}
@Override
public Advice getAdvice() {
return this.advice;
}
@Override
public String toString() {
return getClass().getName() + ": advice [" + getAdvice() + "]";
}
}
DefaultPointcutAdvisor是功能最强大的Advisor,保存了Advice和Pointcut对象,同时具备操作这两个对象的能力:
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
// 保存Advisor关联的Pointcut对象
private Pointcut pointcut = Pointcut.TRUE;
// 获取空的Advisor,在使用前必须指定Pointcut对象和Advice对象。
public DefaultPointcutAdvisor() {
}
// 根据指定Advice和默认的Pointcut生成Advisor对象,使用默认Pointcut时,Advice将匹配所有方法。
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
// 根据指定Pointcut和Advice生成Advisor对象
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
setAdvice(advice);
}
// setter and getter
public void setPointcut(@Nullable Pointcut pointcut) {
this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public String toString() {
return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
}
}
Pt3.5 Advised
Advised是对整个代理执行的抽象,创建代理的工厂ProxyFactory,Bean工厂BeanFactory都在该接口中保存。该接口维护了一些代理必要的属性,以及对 Advice,Advisor,Interceptor 的操作,任何被 Spring 代理的对象都可以强转为 Advised。
从Advised类图中可以看到,实现类ProxyFactory是创建AOP代理类的工厂。
(1) TargetClassAware
接口TargetClassAware表明代理类拥有对目标代理类的操作权限git,具体可以看Aware接口的介绍。
(2) Advised
Advised是核心接口,拥有对代理工厂、目标工厂、目标类的操作能力。
// Advised 接口的实现着主要是代理生成的对象与AdvisedSupport (Advised的支持器)
public interface Advised extends TargetClassAware {
// 这个 frozen 决定是否 AdvisedSupport 里面配置的信息是否改变
boolean isFrozen();
// 是否代理指定的类, 而不是一些 Interface
boolean isProxyTargetClass();
// 返回代理的接口
Class<?>[] getProxiedInterfaces();
// 判断这个接口是否是被代理的接口
boolean isInterfaceProxied(Class<?> intf);
// 设置代理的目标对象
void setTargetSource(TargetSource targetSource);
// 获取代理的对象
TargetSource getTargetSource();
// 判断是否需要将 代理的对象暴露到 ThreadLocal中, 而获取对应的代理对象则通过 AopContext 获取
void setExposeProxy(boolean exposeProxy);
// 返回是否应该暴露 代理对象
boolean isExposeProxy();
// 设置 Advisor 是否已经在前面过滤过是否匹配 Pointcut (极少用到)
void setPreFiltered(boolean preFiltered);
// 获取 Advisor 是否已经在前面过滤过是否匹配 Pointcut (极少用到)
boolean isPreFiltered();
// 获取所有的 Advisor
Advisor[] getAdvisors();
// 增加 Advisor 到链表的最后
void addAdvisor(Advisor advisor) throws AopConfigException;
// 在指定位置增加 Advisor
void addAdvisor(int pos, Advisor advisor) throws AopConfigException;
// 删除指定的 Advisor
boolean removeAdvisor(Advisor advisor);
// 删除指定位置的 Advisor
void removeAdvisor(int index) throws AopConfigException;
// 返回 Advisor 所在位置de index
int indexOf(Advisor advisor);
// 将指定的两个 Advisor 进行替换
boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;
// 增加 Advice <- 这个Advice将会包裹成 DefaultPointcutAdvisor
void addAdvice(Advice advice) throws AopConfigException;
// 在指定 index 增加 Advice <- 这个Advice将会包裹成 DefaultPointcutAdvisor
void addAdvice(int pos, Advice advice) throws AopConfigException;
// 删除给定的 Advice
boolean removeAdvice(Advice advice);
// 获取 Advice 的索引位置
int indexOf(Advice advice);
// 将 ProxyConfig 通过 String 形式返回
String toProxyConfigString();
}
(3) ProxyConfig
保存了创建代理类的配置属性。
// 创建AOP代理类的配置
public class ProxyConfig implements Serializable {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = -8409359707199703185L;
// 标记是否直接对目标类进行代理,而不是通过接口产生代理
// 或者说,标记是否使用CGLIB动态代理,true,表示使用CGLIB的方式产生代理对象,false,表示使用JDK动态代理
private boolean proxyTargetClass = false;
// 标记是否进行优化。启动优化通常意味着在代理对象被创建后,增强的修改将不会生效,因此默认值为false。
// 如果exposeProxy设置为true,即使optimize为true也会被忽略。
private boolean optimize = false;
// 标记是否需要阻止通过该配置创建的代理对象转换为Advised类型,默认值为false,表示代理对象可以被转换为Advised类型
boolean opaque = false;
// 标记代理对象是否应该被aop框架通过AopContext以ThreadLocal的形式暴露出去。
// 当一个代理对象需要调用它自己的另外一个代理方法时,这个属性将非常有用。默认是是false,以避免不必要的拦截。
boolean exposeProxy = false;
// 标记该配置是否需要被冻结,如果被冻结,将不可以修改增强的配置。
// 当我们不希望调用方修改转换成Advised对象之后的代理对象时,这个配置将非常有用。
private boolean frozen = false;
// setter and getter
public void setProxyTargetClass(boolean proxyTargetClass) {
this.proxyTargetClass = proxyTargetClass;
}
public boolean isProxyTargetClass() {
return this.proxyTargetClass;
}
public void setOptimize(boolean optimize) {
this.optimize = optimize;
}
public boolean isOptimize() {
return this.optimize;
}
public void setOpaque(boolean opaque) {
this.opaque = opaque;
}
public boolean isOpaque() {
return this.opaque;
}
public void setExposeProxy(boolean exposeProxy) {
this.exposeProxy = exposeProxy;
}
public boolean isExposeProxy() {
return this.exposeProxy;
}
public void setFrozen(boolean frozen) {
this.frozen = frozen;
}
public boolean isFrozen() {
return this.frozen;
}
// 浅拷贝
public void copyFrom(ProxyConfig other) {
Assert.notNull(other, "Other ProxyConfig object must not be null");
this.proxyTargetClass = other.proxyTargetClass;
this.optimize = other.optimize;
this.exposeProxy = other.exposeProxy;
this.frozen = other.frozen;
this.opaque = other.opaque;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("proxyTargetClass=").append(this.proxyTargetClass).append("; ");
sb.append("optimize=").append(this.optimize).append("; ");
sb.append("opaque=").append(this.opaque).append("; ");
sb.append("exposeProxy=").append(this.exposeProxy).append("; ");
sb.append("frozen=").append(this.frozen);
return sb.toString();
}
}
(4) AdvisedSupport
AdvisedSupport是用于生成Aop代理类的辅助类,保存了目标类、Advisor和method之间的关系。
public class AdvisedSupport extends ProxyConfig implements Advised {
/** use serialVersionUID from Spring 2.0 for interoperability. */
private static final long serialVersionUID = 2651364800145442165L;
// 默认空的TargetSource
public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;
TargetSource targetSource = EMPTY_TARGET_SOURCE;
/** Whether the Advisors are already filtered for the specific target class. */
// 当前Advisor是否已经过滤了目标类
private boolean preFiltered = false;
/** The AdvisorChainFactory to use. */
AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
/** Cache with Method as key and advisor chain List as value. */
// 缓存method和Advisor之间关系
private transient Map<MethodCacheKey, List<Object>> methodCache;
// 目标代理类继承的接口
private List<Class<?>> interfaces = new ArrayList<>();
// 关联的Advisor
private List<Advisor> advisors = new ArrayList<>();
private Advisor[] advisorArray = new Advisor[0];
// 方法基本都是对于属性的操作
// ......
}
(5) ProxyCreatorSupport
生成代理类的辅助类ProxyCreatorSupport。
public class ProxyCreatorSupport extends AdvisedSupport {
// AOP代理类生成工厂,用于创建代理类。
private AopProxyFactory aopProxyFactory;
// 监听Advisor的变化
private final List<AdvisedSupportListener> listeners = new ArrayList<>();
// 第一个AopProxyu对象生成时置为True
private boolean active = false;
public ProxyCreatorSupport() {
this.aopProxyFactory = new DefaultAopProxyFactory();
}
public ProxyCreatorSupport(AopProxyFactory aopProxyFactory) {
Assert.notNull(aopProxyFactory, "AopProxyFactory must not be null");
this.aopProxyFactory = aopProxyFactory;
}
// aopProxyFactory的setter/getter
public void setAopProxyFactory(AopProxyFactory aopProxyFactory) {
Assert.notNull(aopProxyFactory, "AopProxyFactory must not be null");
this.aopProxyFactory = aopProxyFactory;
}
public AopProxyFactory getAopProxyFactory() {
return this.aopProxyFactory;
}
// 操作AdvisedSupportListener
public void addListener(AdvisedSupportListener listener) {
Assert.notNull(listener, "AdvisedSupportListener must not be null");
this.listeners.add(listener);
}
public void removeListener(AdvisedSupportListener listener) {
Assert.notNull(listener, "AdvisedSupportListener must not be null");
this.listeners.remove(listener);
}
// 获取AopProxy,用于生成Bean的代理类
protected final synchronized AopProxy createAopProxy() {
// 第一个AopProxy被创建时,执行以下激活操作。
if (!this.active) {
activate();
}
// 获取AopProxy,用于生成Bean的代理类
return getAopProxyFactory().createAopProxy(this);
}
// 激活AopProxy配置。
private void activate() {
this.active = true;
for (AdvisedSupportListener listener : this.listeners) {
listener.activated(this);
}
}
// 监听advice变更事件
@Override
protected void adviceChanged() {
super.adviceChanged();
synchronized (this) {
if (this.active) {
for (AdvisedSupportListener listener : this.listeners) {
listener.adviceChanged(this);
}
}
}
}
protected final synchronized boolean isActive() {
return this.active;
}
}
(6) ProxyFactory
ProxyFactory是生成目标AOP代理类的工厂类。
public class ProxyFactory extends ProxyCreatorSupport {
public ProxyFactory() {
}
// 根据目标类生成代理类
public ProxyFactory(Object target) {
setTarget(target);
setInterfaces(ClassUtils.getAllInterfaces(target));
}
// 根据目标接口生成代理类
public ProxyFactory(Class<?>... proxyInterfaces) {
setInterfaces(proxyInterfaces);
}
public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {
addInterface(proxyInterface);
addAdvice(interceptor);
}
public ProxyFactory(Class<?> proxyInterface, TargetSource targetSource) {
addInterface(proxyInterface);
setTargetSource(targetSource);
}
// 生成代理类
public Object getProxy() {
return createAopProxy().getProxy();
}
// 基于指定的ClassLoader和Factory设置,创建AOP代理类。
public Object getProxy(@Nullable ClassLoader classLoader) {
// createAopProxy():获取AOP代理类,有JdkDynamicAopProxy和CglibAopProxy两种。
// getProxy:使用AOP代理类,生成目标Bean的代理类。
// 这里假定使用JdkDynamicAopProxy的方式执行getProxy方法
return createAopProxy().getProxy(classLoader);
}
// 生成代理类
public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {
return (T) new ProxyFactory(proxyInterface, interceptor).getProxy();
}
// 生成代理类
@SuppressWarnings("unchecked")
public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource) {
return (T) new ProxyFactory(proxyInterface, targetSource).getProxy();
}
// 生成代理类
public static Object getProxy(TargetSource targetSource) {
if (targetSource.getTargetClass() == null) {
throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class");
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(targetSource);
proxyFactory.setProxyTargetClass(true);
return proxyFactory.getProxy();
}
}
Pt3.6 Pointcut
Pointcut是AOP中非常重要的一个类,它定义了AOP切点逻辑,约定了Advice通知的时机。
我们先来看看其中一个实现类AspectJExpressionPointcut类的相关类图。
从类图上可以看出,AspectJExpressionPointcut主要有4个顶层接口:
-
Pointcut:定义和切点相关的能力。
-
Aware:标记当前类具备某种行为的感知能力。
-
MethodMatcher:用来匹配mehtod是否匹配切点的规则,从而决定是否需要通知Advice。
-
ClassFilter:用来匹配Class是否匹配切点规则。
ClassFilter限定在类级别上,MethodMatcher限定在方法级别上。
首先来看Pointcut顶层接口的能力:
// 切点的顶层抽象接口
public interface Pointcut {
// 返回当前Pointcut的ClassFilter,用于匹配Class是否符合切点逻辑。
ClassFilter getClassFilter();
// 返回当前Pointcut的MethodMatcher对象,用于匹配method规则。
MethodMatcher getMethodMatcher();
// 默认值,如果返回此结果表明所有method都会被匹配成功。
Pointcut TRUE = TruePointcut.INSTANCE;
}
// 匹配规则使用String表达式的Pointcut
public interface ExpressionPointcut extends Pointcut {
// 返回切点规则表达式
@Nullable
String getExpression();
}
public abstract class AbstractExpressionPointcut implements ExpressionPointcut, Serializable {
// 切点位置
@Nullable
private String location;
// 切点匹配规则表达式
@Nullable
private String expression;
// setter and getter
public void setLocation(@Nullable String location) {
this.location = location;
}
@Nullable
public String getLocation() {
return this.location;
}
public void setExpression(@Nullable String expression) {
this.expression = expression;
try {
onSetExpression(expression);
}
catch (IllegalArgumentException ex) {
// Fill in location information if possible.
if (this.location != null) {
throw new IllegalArgumentException("Invalid expression at location [" + this.location + "]: " + ex);
}
else {
throw ex;
}
}
}
protected void onSetExpression(@Nullable String expression) throws IllegalArgumentException {
}
@Override
@Nullable
public String getExpression() {
return this.expression;
}
}
最终实现类是AspectJExpressionPointcut类:
// Pointcut实现类。
@SuppressWarnings("serial")
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
......
}
Pt3.7 ClassFilter
比较简单,直接看源码:
// ClassFilter限定在类级别上,判断是否符合Pointcut规则。
@FunctionalInterface
public interface ClassFilter {
// true表示能够匹配。那就会进行织入的操作
boolean matches(Class<?> clazz);
// 默认值。会匹配所有的类,TrueClassFilter不是public得class,所以只是Spring内部自己使用的。
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
Pt3.8 MethodMatcher
MethodMatcher用于定义Pointcut匹配method的逻辑:
// Pointcut匹配逻辑:判断方法是否符合Pointcut规则,执行Advice通知。
public interface MethodMatcher {
// 这个称为静态匹配:在匹配条件不是太严格时使用,可以满足大部分场景的使用
boolean matches(Method method, Class<?> targetClass);
// 这个称为动态匹配(运行时匹配): 它是严格的匹配。在运行时动态的对参数的类型进行匹配
boolean matches(Method method, Class<?> targetClass, Object... args);
// 两个方法的分界线就是boolean isRuntime()方法,步骤如下
// 1、先调用静态匹配,若返回true。此时就会继续去检查isRuntime()的返回值
// 2、若isRuntime()还返回true,那就继续调用动态匹配
// (若静态匹配都匹配上,动态匹配那铁定更匹配不上得~~~~)
boolean isRuntime();
// 默认值
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
实现接口:
// 校验方法是否匹配Pointcut规则
public interface IntroductionAwareMethodMatcher extends MethodMatcher {
boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions);
}
Pt3.9 Aware
我们在介绍下Aware接口。
打开Aware接口可以看到,他是一个空接口,没有任何实现。他标识的其实是一种能力,Spring容器中的Bean能够具备感知某一对象的能力。
// Aware是顶层超类,用于标识是否具备一种能力。
public interface Aware {
}
比如,BeanFactoryAware是继承了Aware的接口,它就代表Bean对归属Spring IoC容器的感知能力。
// 实现了BeanFactoryAware接口的类,能够感知到他所属的BeanFactory。直白点,就是Bean能够获得他所属的BeanFactory对象。
// 这就是Aware接口的能力,他使得继承类能够具备某一种感知的能力。
public interface BeanFactoryAware extends Aware {
// 设置当前Bean所属的BeanFactory实例
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
还有TargetClassAware接口:
// 该接口使得,对应的代理类拥有感知TargetClass的能力,就是能够操作目标代理类。
public interface TargetClassAware {
// 获得目标代理类。
@Nullable
Class<?> getTargetClass();
}
Pt4 Spring AOP初始化剖析
Pt4.1 寻找入口
在AOP基础部分中,介绍过AOP的原理是生成原始Bean对象的代理类,从而在执行过程中通过动态代理完成逻辑横切。
所以AOP的初始化是在Bean初始化完成之后发生的。在DI部分,通过调用BeanFactory.getBean()触发Bean的初始化和依赖注入,当整个Bean初始化逻辑之心完成后,会继续执行AOP初始化处理,为Bean创建AOP代理类。
DI操作是通过调用#getBean()触发的,其核心逻辑是在AbstractAutowireCapableBeanFactory#doCreateBean()执行。我们跳过IoC、DI和MVC部分的详细逻辑介绍,直接从doCreateBean()来找AOP的入口。
先来看#doCreateBean()实例化逻辑:
// org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// ......
// 跳过DI前后其他逻辑,这部分在DI部分已经详细介绍过。
// 不管Bean的Scope是那种,最终都是通过#createBean()·。
return createBean(beanName, mbd, args);
// ......
}
还没到AOP的地方,继续往下:
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// 真正完成指定Bean对象创建的方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ......
// 跳过DI前后其他逻辑,这部分在DI部分已经详细介绍过。
// Bean的处理只要分为三个步骤:
// 1. 实例化,执行构造器;
// 2. 依赖注入;
// 3. 调用init-method;
// 到这里,DI已经完成Bean的实例化操作,这是第一步,我们继续往下看。
// 2、对Bean进行依赖注入
populateBean(beanName, mbd, instanceWrapper);
// 3、Bean实例对象依赖注入完成后,开始对Bean实例对象进行初始化,为Bean实例对象应用BeanPostProcessor后置回调处理
// AOP的逻辑在这里执行
exposedObject = initializeBean(beanName, exposedObject, mbd);
// ......
}
找到#initializeBean,这里才是真正开始的地方:
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
// 初始化容器创建的Bean实例对象,为其添加BeanPostProcessor后置处理器
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// JDK安全机制验证权限
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
// 为Bean实例对象包装相关属性:类名称、类加载器、所属容器等。
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 1、调用BeanPostProcessor的回调方法,执行Bean初始化之前的操作。
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 2、调用Bean的Init-Method方法,完成Bean的初始化操作。
// 这个Bean的初始化方法是在Spring Bean定义配置文件中通过init-method属性指定的。
// 这里不是新建Bean的实例操作,此时的Bean已经完成实例化处理,生成了Bean的实例对象,也完成了属性的设置。
// 如果Bean定义中配置了init-method,这里将执行该方法完成Bean配置中所有init动作。
invokeInitMethods(beanName, wrappedBean, mbd);
// 到此为止,Bean实例的初始化操作已经完成,创建了Bean对象,完成了属性的依赖注入,执行了自定义的init-method方法。
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 3、调用BeanPostProcessor的回调方法,执行Bean初始化之后的操作。
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
代码里分为三步:
-
调用BeanPostProcessor的回调方法,执行Bean初始化之前的操作。
-
调用Bean的Init-Method方法,完成Bean的初始化操作。
-
调用BeanPostProcessor的回调方法,执行Bean初始化之后的操作。
AOP是在Bean完成初始化之后触发的,前两部Bean还没有完成初始化,所以真正跟AOP相关的操作,是在第3步开始的:
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
// 调用BeanPostProcessor的回调方法,执行Bean初始化之后的操作。
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
// 遍历容器为所创建的Bean添加的所有BeanPostProcessor后置处理器。
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 调用Bean实例所有定义的BeanPostProcessor初始化之后回调操作,完成初始化后的自定义操作。
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
Pt4.2 选择代理策略
执行BeanPostProcessor的后置回调方法,AOP的BeanPostProcessor实现类很多,其中AbstractAutoProxyCreator是非常重要的一个。
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
wrapIfNecessary()是非常核心的方法,负责处理AOP代理。
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
/**
* 如果给定的Bean符合AOP条件,则生成代理类。
*
* @param bean 原始Bean实例对象
* @param beanName Bean的名称
* @param cacheKey 从Bean信息得到的唯一缓存Key
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 判断是否应该代理这个Bean
if (Boolean以上是关于10. Spring AOP源码解析的主要内容,如果未能解决你的问题,请参考以下文章