Spring-AOP基础与原理

Spring AOP(面向切面编程)是 Spring 框架中用于实现横切关注点分离的重要模块,它允许开发者将诸如日志记录、事务管理等通用功能从业务逻辑中分离出来。

Spring AOP 主要通过动态代理技术来实现。当一个 Bean 被标记为需要应用 AOP 切面时,Spring 会为其创建一个代理对象而不是直接使用目标对象。这个代理对象可以是 JDK 动态代理或 CGLIB 代理,取决于被代理的目标对象是否实现了接口。

  • JDK 动态代理:如果目标类实现了至少一个接口,则 Spring 使用 JDK 提供的 java.lang.reflect.Proxy 来创建代理。代理对象会实现与目标对象相同的接口,并拦截对接口方法的调用。
  • CGLIB 代理:如果目标类没有实现任何接口,或者明确指定了使用 CGLIB,则 Spring 会利用 CGLIB 库生成目标类的一个子类作为代理。这种方式下,代理不仅能够拦截接口方法,也能拦截类中的非 final 方法。

核心概念

  • 切面(Aspect):横切关注点的模块化封装,包含通知(Advice)和切入点(Pointcut)。
  • 切入点(Pointcut):定义哪些方法或代码块需要被拦截(通过表达式匹配,如 execution(* com.ares.Controller..*(..)))。
  • 通知(Advice):在切入点处执行的代码,包括五种类型:
    • 前置通知(Before):目标方法执行前触发。
    • 后置通知(After Returning/@After):目标方法正常执行后触发。
    • 异常通知(AfterThrowing/@After):目标方法抛出异常时触发。
    • 最终通知(After):无论是否异常,目标方法执行后触发。
    • 环绕通知(Around):包裹目标方法,在其前后均执行逻辑。
  • 目标对象(Target Object):被代理的对象,其方法会被增强。
  • AOP代理(Proxy):Spring生成的代理对象,负责拦截方法调用并执行通知逻辑。

应用 - API请求日志

  • 实现记录请求API日志:请求参数,RemoteIp,请求时间,traceID等
  • MDC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
@Aspect
@Component
public class RequestsLoggerAspect {

private final Logger logger = LoggerFactory.getLogger("api");
private final Gson gson;
private final String TRACE_ID = "traceId";

public RequestsLoggerAspect(Gson gson) {
this.gson = gson;
}


@Pointcut("execution(* com.*.controller.*.*(..))")
private void requestLogger() {}

@Around("requestLogger()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
ServletRequestAttributes requestAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return joinPoint.proceed();
}

HttpServletRequest request = requestAttributes.getRequest();
String reqId = request.getHeader(TRACE_ID);

if (!StringUtils.hasText(reqId) || Objects.equals(reqId, "0")) {
reqId = getTraceId();
}
MDC.put(TRACE_ID, reqId);

// 通过请求获取url,ip
String url = request.getRequestURL().toString();
String remoteIp = getIpAddr(request);

// 获取请求头信息
Map<String, Object> header = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
String value = request.getHeader(key);
header.put(key, value);
}

// 获取方法所在的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取参数名
String[] parameterNamesArgs =
((org.aspectj.lang.reflect.MethodSignature) joinPoint.getSignature()).getParameterNames();
// 获取参数值
Object[] args = joinPoint.getArgs();

joinPoint.getSignature().getDeclaringTypeName();

Map<String, Object> params = new HashMap<>();
for (int i = 0; i < parameterNamesArgs.length; i++) {
params.put(parameterNamesArgs[i], args[i]);
}

Object result = null;
try {
logger.info(
"start:{} | remote ip:{} | url:{} | class:{} | func :{} | params:{} | header:{}",
start, remoteIp, url, className, methodName, gson.toJson(params), gson.toJson(header));
result = joinPoint.proceed();
} catch (Exception e) {
throw e;
} finally {
long end = System.currentTimeMillis();
long cost = end - start;
logger.info("end:{} | cost :{}ms | result:{}", end, cost, gson.toJson(result));
MDC.remove(TRACE_ID);
}
return result;
}

private String getIpAddr(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if (!StringUtils.hasText(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (!StringUtils.hasText(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (!StringUtils.hasText(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (!StringUtils.hasText(ipAddress) && ipAddress.length() > 15) {
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
// 或者这样也行,对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
return ipAddress;
}

private String getTraceId() {
UUID uuid = UUID.randomUUID();
long l = uuid.getMostSignificantBits() >>> 26; // 取高24位
String id = String.valueOf(l);

if (id.length() > 6) {
id = id.substring(0, 6);
} else if (id.length() < 6) {
id = String.format("%0" + 6 + "d", Long.parseLong(id));
}
return Instant.now().toEpochMilli() + id;
}
}

实现原理

Spring AOP 的实现原理是基于动态代理字节码操作的。在编译时, Spring 会使用 AspectJ 编译器将切面代码编译成字节码文件。在运行时, Spring5.0后会使用 Java 动态代理或 Byte Buddy作为默认代理生成代理类,这些代理类会在目标对象方法执行前后插入切面代码,从而实现AOP的功能。Spring AOP 可以使用两种代理方式:JDK动态代理和 Byte Buddy 代理。

1
2
3
4
5
6
7
@EnableAspectJAutoProxy(proxyTargetClass = true)
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class IdGenApplication {
public static void main(String[] args) {
SpringApplication.run(IdGenApplication.class, args);
}
}

或 yaml配置文件:

1
spring.aop.proxy-target-class: true

@EnableAspectJAutoProxy

在spring-boot项目开发中引入spring-boot-starter-aop,通过@Enable模块驱动注解EnableAspectJAutoProxy用于开启AspectJ自动代理:

1
2
3
4
5
6
7
8
9
10
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
// 为true时强制使用 cglib/Byte Buddy
boolean proxyTargetClass() default false;

boolean exposeProxy() default false;
}

@EnableAspectJAutoProxy通过@Import导入了AspectJAutoProxyRegistrarAspectJ自动代理注册器,在这个类中实现了 BeanDefinition 的注册,而后面的过程就是 Bean 的注册过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 往IOC容器里注册了一个类型为AnnotationAwareAspectJAutoProxyCreator
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);
}
}
}
}
@EnableAspectJAutoProxy模块驱动注解往IOC容器中注册了类型为AnnotationAwareAspectJAutoProxyCreator的Bean,Bean名称为org.springframework.aop.config.internalAutoProxyCreator。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

// 判断是否存在 internalAutoProxyCreator
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}

// 在引入spring-boot-starter-aop后,aop自动装配时容器中并没有这个类型的 Bean, 所以直接在 RootBeanDefinition 注册一个
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}

上面的核心逻辑为通过RootBeanDefinition往IOC注册了名称为AUTO_PROXY_CREATOR_BEAN_NAME(常量,值为org.springframework.aop.config.internalAutoProxyCreator ),类型为AnnotationAwareAspectJAutoProxyCreator的Bean。

AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor和BeanFactoryAware接口。实现BeanFactoryAware用于在Bean初始化时注入BeanFactory,而SmartInstantiationAwareBeanPostProcessor接口的父类为InstantiationAwareBeanPostProcessor接口,该接口继承自BeanPostProcessor接口。

在AbstractAutoProxyCreator类实现了InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法(自定义Bean实例化前操作逻辑),以及实现了BeanPostProcessor的postProcessAfterInitialization方法(自定义Bean初始化后操作逻辑)。

创建代理过程

Spring AOP是怎样增强我们定义的目标类TargetClass的?在AbstractAutoProxyCreator#postProcessBeforeInstantiation()实现了具体的代理逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Override
@Nullable
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);

if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}

// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}

return null;
}

  • 通过Bean名称和Bean类型获取该Bean的唯一缓存键名: getCacheKey(beanClass, beanName)

  • 判断当前Bean是否包含在advisedBeans集合中

  • 判断当前 Bean 是否是基础类,isInfrastructureClass(): 这一步主要逻辑:判断当前Bean(TargetClass)是否是Advice,Pointcut,Advisor,AopInfrastructureBean的子类或者是否为切面类(@Aspect注解标注)。

  • 判断是否需要跳过:

  • 通过Bean名称判断是否以AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX(.ORIGINAL)结尾,是的话返回true表示跳过代理。

  • 如果我们自定义了TargetSource,则在此处创建Bean代理,以取代目标Bean的后续默认实例化方式。我们并没有自定义TargetSource,所以直接跳过。

  • getAdvicesAndAdvisorsForBean方法内部主要包含以下这些逻辑:

    • 获取所有的通知方法(切面里定义的各个方法);
    • 通过切点表达式判断这些通知方法是否可为当前Bean所用;
    • 如果有符合的通知方法,则对它们进行排序。
  • 创建代理: createProxy(beanClass, beanName, specificInterceptors, targetSource)

后续从IOC容器中获得的TargetClass就是被代理后的对象,执行代理对象的目标方法的时候,代理对象会执行相应的通知方法链。

生成拦截器链MethodInterceptor

拦截器链 就是在代理对象的某个方法被执行时,从通知方法集合中筛选出适用于该方法的通知,然后封装为拦截器对象集合(类型为MethodInteceptor)。

在上面示例中当执行到:target.targetMethod() 时,会进入到:CglibAopProxy的intercept 中, 也就是说我们的目标对象的目标方法被CglibAopProxy的intercept方法拦截了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();

try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}

// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal; // 获取目标对象目标方法的拦截器链

// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
// 如果拦截器为空,则通过反射方式执行目标方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}

else {
// 如果拦截器不空则创建ReflectiveMethodInvocation对象,执行它的 proceed 方法
// We need to create a method invocation...
retVal = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain).proceed();
}
return processReturnType(proxy, target, method, args, retVal);
}

finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}

getInterceptorsAndDynamicInterceptionAdvice方法中遍历代理对象的通知集合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
* for the given method, based on this configuration.
* @param method the proxied method
* @param targetClass the target class
* @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
*/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
// 创建目标方法的缓存 key
List<Object> cachedInterceptors;

if (this.methodCache != null) {
// Method-specific cache for method-specific pointcuts
MethodCacheKey cacheKey = new MethodCacheKey(method);

cachedInterceptors = this.methodCache.get(cacheKey);
// 第一次,未缓存,创建缓存
if (cachedInterceptors == null) {
//
cachedInterceptors = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cachedInterceptors);
}
}
else {
// Shared cache since there are no method-specific advisors (see below).
cachedInterceptors = this.cachedInterceptors;
if (cachedInterceptors == null) {
cachedInterceptors = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.cachedInterceptors = cachedInterceptors;
}
}
return cachedInterceptors;
}

拦截器链第一个元素类型为ExposeInvocationInterceptor,是默认的拦截器,剩下四个依次为:MethodBeforeAdviceInterceptor、AspectJAfterAdvice、AfterReturningAdviceInterceptor和AspectJAfterThrowingAdvice,它们都是MethodInterceptor的实现类。

获取到了代理对象目标方法的拦截器链后,判断拦截器链不为空时,CglibAopProxy的intercept方法创建CglibMethodInvocation对象,并调用它的proceed方法, 完成拦截器链处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
@Nullable
public Object proceed() throws Throwable {
try {
return super.proceed();
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
......
}
}
}

Spring-AOP基础与原理
http://example.com/2025/06/11/spring-aop基础与原理/
作者
ares
发布于
2025年6月11日
许可协议