---
date: 2019-09-23
---
# 前言
Spring 最核心的功能就是 `IOC 容器 ` 和 `AOP`。本文定位是以最简的方式,分析 `Spring AOP` 源码。
# 基本概念
Spring AOP 代理实现分为 2 种情况:
1. 要代理的类是接口:基于 `JDK 动态代理 ` 实现;
2. 要代理的类不是接口:基于 `CGLIB 动态代理 ` 实现。
Spring AOP ` 只能作用于 Spring bean`,使用了 aspectj 的注解,但是完全是基于 Spring 代码实现。
## 实现原理
Spring AOP 的实现原理是 ` 动态代理 `,具体是什么样的呢?
在 Spring 容器中,我们使用的每个 bean 都是 `BeanDefinition` 的实例,容器会在合适的时机根据 BeanDefinition 的基本信息实例化 bean 对象。
所以比较简单的做法是,Spring 会自动生成代理对象的代理类。我们在获取 bean 时,Spring 容器返回 ` 代理类对象 `,而不是实际的 bean。
## 测试代码
本文代码基于 `Spring Boot` 3.0.2 版本,是一个基于注解的最简调试代码。
注解配置类 AopConfig 如下,功能是对 `yano.spring.service` 包下的所有类的所有方法进行切面,记录调用方法的参数、返回结果、异常结果、耗时。
```java
package yano.spring.config;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@Aspect
public class AopConfig {
@Pointcut("within(yano.spring.service..*)")
public void pointCut() {
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint p) throws Throwable {
Signature signature = p.getSignature();
log.info("调用开始 {}.{}, 参数 {}", signature.getDeclaringTypeName(), signature.getName(), JSON.toJSONString(p.getArgs()));
long startTime = System.currentTimeMillis();
try {
Object result = p.proceed(p.getArgs());
log.info("调用结束 {}.{}, 耗时 {}ms, 返回值:{}", signature.getDeclaringTypeName(), signature.getName(), System.currentTimeMillis() - startTime, JSON.toJSONString(result));
return result;
} catch (Throwable t) {
log.error(String.format("调用异常 %s.%s, 耗时 %dms, 异常:%s", signature.getDeclaringTypeName(), signature.getName(), System.currentTimeMillis() - startTime, t.getMessage()), t);
throw t;
}
}
}
```
Spring 启动类 AppApplication:
```java
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
```
yano.spring.service.ConvertService#convert 用来验证 AOP:
```java
package yano.spring.service;
import org.springframework.stereotype.Service;
@Service
public class ConvertService {
public String convert(int num, int add) {
return (num + add) + "";
}
}
```
测试用例 AopTest:
```java
package test.aop;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import yano.spring.Application;
import yano.spring.service.ConvertService;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class AopTest {
@Autowired
private ConvertService convertService;
@Test
public void convert() {
convertService.convert(1, 2);
}
}
```
输出日志如下,可以看到在调用 yano.spring.service.ConvertService#convert 的前后打印出了入参、耗时、返回值等信息。
```
调用开始 yano.spring.service.ConvertService.convert, 参数 [1,2]
调用结束 yano.spring.service.ConvertService.convert, 耗时 1ms, 返回值:"3"
```
## 使用步骤
1. `@EnableAspectJAutoProxy` 注解开启 AOP;
2. 编写 AOP 的配置类(使用 `@Aspect` 注解);
3. 配置 `Advice`,具体参考 [Spring AOP 官方文档](https://docs.spring.io/spring/docs/2.0.x/reference/aop.html);
4. 配置 `@Pointcut`,匹配 Spring 容器中的 bean。
`@Pointcut` 注解的使用示例如下:
```java
@Pointcut("within(yano.spring.service..*)")
public void pointCut() {
}
```
- `execution`:在使用 Spring AOP 时,这是主要的连接点设计器,用于匹配方法执行连接点。
- `within` :限制匹配在特定类型内的连接点(使用 Spring AOP 时仅执行在匹配类型内声明的方法)。
- `this`:限制匹配在 bean 引用(Spring AOP 代理)是给定类型的实例的连接点(使用 Spring AOP 时执行方法)。
- `target`:限制匹配在目标对象(被代理的应用对象)是给定类型的实例的连接点(使用 Spring AOP 时执行方法)。
- `args`:限制匹配在参数是给定类型实例的连接点(使用 Spring AOP 时执行方法)。
- `@target` - 限制匹配在执行对象的类具有给定类型的注释的连接点(使用 Spring AOP 时执行具有给定注释的类声明的方法)。
- `@args`:限制匹配在实际参数的运行时类型具有给定注释的连接点(使用 Spring AOP 时执行方法)。
- `@within` - 限制匹配在具有给定注释的类型内的连接点(使用 Spring AOP 时执行具有给定注释的类型内声明的方法)。
- `@annotation`:限制匹配在连接点主题(使用 Spring AOP 时执行的方法)具有给定注释的连接
>💡 Tips:上面匹配中,通常 "." 代表一个包名,".." 代表包及其子包,方法参数任意匹配使用两个点 ".."。
# 源码分析
## @EnableAspectJAutoProxy 开启 AOP
在 AppApplication 启动类上要加入 `@EnableAspectJAutoProxy` 注解开启 AOP,查看该注解源码,其 proxyTargetClass() 是在 AspectJAutoProxyRegistrar 类中调用,而 AspectJAutoProxyRegistrar 是接口 ImportBeanDefinitionRegistrar 的实现类。再往上追根溯源,可以看到是在接口 ConfigurableApplicationContext 中 void refresh() 调用。
@EnableAspectJAutoProxy 注解定义:
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*/
boolean proxyTargetClass() default false;
/**
* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {@code AopContext} access will work.
* @since 4.3.1
*/
boolean exposeProxy() default false;
}
```
```java
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
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);
}
}
}
}
```
## IOC 容器管理 AOP 实例
在创建 bean 时,会调用 AbstractAutowireCapableBeanFactory#doCreateBean(...)。
```java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// 初始化 bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 1. 创建实例
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
...
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 2. 装载属性
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 3. 初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
...
}
```
着重看第 3 步 initializeBean(...) 方法:
```java
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction