Appearance
AOP原理分析
在前面的几篇文章中,我们介绍了Spring AOP的基本概念、配置方式和高级特性。本文将深入探讨Spring AOP的实现原理,帮助大家理解Spring AOP是如何工作的,以及在使用过程中可能遇到的一些限制。
1. Spring AOP的代理机制
Spring AOP是基于代理(Proxy)实现的。代理是一种设计模式,它允许在不改变原始对象代码的情况下,通过引入一个代理对象来控制对原始对象的访问,从而实现在方法调用前后添加额外行为的目的。
Spring AOP支持两种代理机制:
1.1 JDK动态代理
JDK动态代理是Java标准库提供的代理机制,它要求代理的类必须实现至少一个接口。
工作原理:
- 创建一个实现
InvocationHandler
接口的处理器 - 使用
Proxy.newProxyInstance()
方法创建代理实例 - 当调用代理对象的方法时,调用会被转发到
InvocationHandler.invoke()
方法
简化的JDK动态代理示例:
java
// 定义接口
interface UserService {
void save(User user);
}
// 实现类
class UserServiceImpl implements UserService {
public void save(User user) {
System.out.println("保存用户: " + user.getName());
}
}
// 调用处理器
class AopInvocationHandler implements InvocationHandler {
private final Object target;
public AopInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置通知: 方法执行前");
// 调用目标方法
Object result = method.invoke(target, args);
System.out.println("后置通知: 方法执行后");
return result;
}
}
// 创建代理
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[] { UserService.class },
new AopInvocationHandler(userService)
);
// 调用代理方法
proxy.save(new User("张三"));
1.2 CGLIB代理
CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,Spring AOP使用它为没有实现接口的类创建代理。
工作原理:
- CGLIB通过生成目标类的子类来创建代理
- 在子类中重写父类的方法,添加增强逻辑
- 使用CGLIB的
Enhancer
类来创建代理对象
简化的CGLIB代理示例:
java
// 目标类 (没有实现任何接口)
class UserService {
public void save(User user) {
System.out.println("保存用户: " + user.getName());
}
}
// 方法拦截器
class AopMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置通知: 方法执行前");
// 调用原始方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置通知: 方法执行后");
return result;
}
}
// 创建代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new AopMethodInterceptor());
UserService proxy = (UserService) enhancer.create();
// 调用代理方法
proxy.save(new User("张三"));
1.3 Spring如何选择代理方式
Spring在创建AOP代理时,会按照以下规则选择代理方式:
- 如果目标类实现了至少一个接口,默认使用JDK动态代理
- 如果目标类没有实现任何接口,使用CGLIB代理
- 如果强制指定使用CGLIB(通过配置
proxyTargetClass=true
),则始终使用CGLIB代理
java
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB代理
@Configuration
public class AppConfig {
// ...
}
2. Spring AOP的初始化流程
Spring AOP的实现是通过一系列的初始化步骤来完成的,下面我们来详细分析这个流程。
2.1 AOP配置解析
当Spring容器启动时,它会首先解析配置(XML或注解)并注册所有的bean定义。对于AOP配置:
- 基于XML的配置:Spring解析
<aop:config>
和<aop:aspect>
等元素,创建对应的advisor和advice - 基于注解的配置:Spring识别
@Aspect
注解的类,并解析其中的@Pointcut
、@Before
等注解
在解析过程中,Spring会将切面、切点和通知信息转换为内部表示,主要是创建各种Advisor
、Advice
和Pointcut
对象。
2.2 代理创建过程
Spring在创建bean实例时,会执行以下步骤来创建AOP代理:
- 实例化原始bean:首先,Spring会像普通bean一样实例化目标对象
- 检查是否需要代理:Spring检查该bean是否匹配任何切点表达式
- 创建AOP代理:如果需要代理,Spring会使用
ProxyFactory
创建代理对象
代理创建的关键类和接口包括:
AbstractAutoProxyCreator
:自动代理创建器的抽象基类DefaultAdvisorAutoProxyCreator
:基于Advisor的自动代理创建器AnnotationAwareAspectJAutoProxyCreator
:处理@AspectJ注解的代理创建器ProxyFactory
:创建AOP代理的工厂类AopProxy
:代理生成的策略接口JdkDynamicAopProxy
:使用JDK动态代理的实现CglibAopProxy
:使用CGLIB的实现
2.3 初始化流程图
Spring AOP初始化流程可以简化为以下步骤:
加载Spring配置
↓
解析AOP配置(XML或注解)
↓
注册Bean后处理器(如AnnotationAwareAspectJAutoProxyCreator)
↓
实例化Bean
↓
Bean后处理阶段 → AbstractAutoProxyCreator.postProcessAfterInitialization()
| ↓
| 找到与Bean匹配的Advisor
| ↓
| 创建ProxyFactory并设置Advisor
| ↓
| 根据条件选择代理方式(JDK或CGLIB)
| ↓
| 创建并返回代理对象
↓
返回代理对象(替代原始Bean)
3. AOP代理的调用链路
当我们调用一个被Spring AOP代理的方法时,调用会经过一系列处理步骤,下面我们来看看这个调用链路。
3.1 JDK动态代理调用链路
调用代理对象方法
↓
JdkDynamicAopProxy.invoke()
↓
获取匹配当前方法的拦截器链
↓
创建ReflectiveMethodInvocation
↓
调用ReflectiveMethodInvocation.proceed()
↓
依次执行拦截器链中的拦截器
|
├→ ExposeInvocationInterceptor
├→ 前置通知拦截器
├→ 环绕通知拦截器开始部分
|
↓
反射调用目标方法
|
↓
|
├→ 环绕通知拦截器结束部分
├→ 返回通知拦截器
├→ 异常通知拦截器(如果有异常)
├→ 后置通知拦截器
|
↓
返回方法结果或抛出异常
3.2 CGLIB代理调用链路
CGLIB代理的调用链路与JDK动态代理类似,主要区别在于入口是DynamicAdvisedInterceptor.intercept()
方法:
调用代理对象方法
↓
CglibAopProxy.DynamicAdvisedInterceptor.intercept()
↓
获取匹配当前方法的拦截器链
↓
创建CglibMethodInvocation
↓
调用CglibMethodInvocation.proceed()
↓
依次执行拦截器链中的拦截器
|
├→ ExposeInvocationInterceptor
├→ 前置通知拦截器
├→ 环绕通知拦截器开始部分
|
↓
调用目标方法
|
↓
|
├→ 环绕通知拦截器结束部分
├→ 返回通知拦截器
├→ 异常通知拦截器(如果有异常)
├→ 后置通知拦截器
|
↓
返回方法结果或抛出异常
3.3 拦截器链的构建
拦截器链是AOP调用过程中的核心概念,它包含了所有与当前方法匹配的通知,按照特定顺序排列:
- 获取所有适用的Advisor:根据方法和目标类找到所有匹配的Advisor
- 转换为MethodInterceptor:将Advisor中的Advice转换为MethodInterceptor
- 排序:按照优先级对拦截器进行排序
- 执行:按照特定顺序执行拦截器链
拦截器链的排序遵循以下原则:
- 前置通知:优先级高的先执行
- 后置、返回、异常通知:优先级低的先执行
- 环绕通知:遵循与前置和后置通知相同的规则
4. 切点表达式的解析与匹配
Spring AOP中的切点表达式决定了哪些方法会被代理,下面我们来看看切点表达式是如何解析和匹配的。
4.1 切点表达式解析过程
- 字符串形式的切点表达式被传递给
PointcutParser
- 解析器将表达式解析为语法树
- 生成适当的
Pointcut
对象- 对于AspectJ表达式,生成
AspectJExpressionPointcut
- 对于自定义表达式,使用相应的
Pointcut
实现
- 对于AspectJ表达式,生成
4.2 匹配过程
切点匹配分为两个阶段:
静态匹配:根据方法签名和类信息进行匹配,主要检查:
- 类是否匹配(使用
ClassFilter
) - 方法签名是否匹配(使用
MethodMatcher.matches(Method, Class)
)
- 类是否匹配(使用
运行时匹配(可选):如果
MethodMatcher.isRuntime()
返回true
,则进行运行时匹配,检查:- 方法参数是否匹配(使用
MethodMatcher.matches(Method, Class, Object[])
)
- 方法参数是否匹配(使用
4.3 常见切点表达式的解析
以下是一些常见切点表达式的解析示例:
execution:
execution(public * com.example.service.*.*(..))
解析为:
ClassFilter
: 检查类是否属于com.example.service
包MethodMatcher
: 检查方法是否是public,以及方法名和参数
@annotation:
@annotation(org.springframework.transaction.annotation.Transactional)
解析为:
ClassFilter
: 返回true
(不限制类)MethodMatcher
: 检查方法是否带有@Transactional
注解
within:
within(com.example.service.*)
解析为:
ClassFilter
: 检查类是否属于com.example.service
包直接子类MethodMatcher
: 返回true
(不限制方法)
5. Spring AOP的内部核心类
了解Spring AOP的核心类有助于深入理解其工作原理。以下是一些重要的核心类:
5.1 代理创建相关
ProxyConfig
: 代理配置的基类AdvisedSupport
: 代理配置的核心类,包含Advice和Advisor列表ProxyFactory
: 创建AOP代理的工厂ProxyFactoryBean
: 用于在XML配置中创建代理的FactoryBeanAbstractAutoProxyCreator
: 自动创建代理的BeanPostProcessor
5.2 切点和通知相关
Pointcut
: 切点接口,定义如何选择连接点ClassFilter
: 过滤类MethodMatcher
: 匹配方法
Advice
: 通知接口,定义在连接点处要执行的行为BeforeAdvice
: 前置通知AfterReturningAdvice
: 返回通知ThrowsAdvice
: 异常通知MethodInterceptor
: 环绕通知
Advisor
: 将Pointcut和Advice组合起来的接口PointcutAdvisor
: 基于切点的Advisor
5.3 AOP调用相关
AopProxy
: 代理创建策略接口JdkDynamicAopProxy
: JDK动态代理实现CglibAopProxy
: CGLIB代理实现
MethodInvocation
: 方法调用的表示ReflectiveMethodInvocation
: 反射方法调用CglibMethodInvocation
: CGLIB方法调用
AopContext
: 提供对当前代理对象的访问
5.4 AspectJ集成相关
AspectJExpressionPointcut
: 使用AspectJ表达式的切点AspectJPointcutAdvisor
: 使用AspectJ表达式的AdvisorAspectJAdvisorFactory
: 创建基于@AspectJ注解的AdvisorReflectiveAspectJAdvisorFactory
: AspectJAdvisorFactory的反射实现
6. Spring AOP的限制与解决方案
Spring AOP虽然强大,但它也有一些固有的限制,了解这些限制和可能的解决方案对于正确使用AOP至关重要。
6.1 自调用问题(Self-invocation)
限制:当一个bean内部的方法直接调用同一个bean的另一个方法时,AOP通知不会被触发。这是因为内部调用不经过代理对象。
java
@Service
public class UserService {
@Transactional // 不会生效
public void updateUser(User user) {
// 业务逻辑
}
public void process() {
User user = new User();
// 直接调用,不会触发事务
this.updateUser(user);
}
}
解决方案:
自注入代理:
java@Service public class UserService { @Autowired @Lazy // 避免循环依赖 private UserService self; @Transactional public void updateUser(User user) { // 业务逻辑 } public void process() { User user = new User(); // 通过代理调用,会触发事务 self.updateUser(user); } }
使用AopContext(需要启用exposeProxy):
java@EnableAspectJAutoProxy(exposeProxy = true) @Configuration public class AppConfig { // ... } @Service public class UserService { @Transactional public void updateUser(User user) { // 业务逻辑 } public void process() { User user = new User(); // 通过AopContext获取代理 ((UserService) AopContext.currentProxy()).updateUser(user); } }
拆分为两个类:将相关功能拆分到不同的类中,通过注入调用
使用AspectJ代替Spring AOP:AspectJ不存在自调用问题
6.2 final方法和类
限制:Spring AOP不能代理final方法或类,因为:
- JDK动态代理基于接口,不受影响
- CGLIB通过创建子类实现代理,不能继承final类或重写final方法
解决方案:
- 避免使用final关键字
- 确保final方法不需要AOP功能
- 使用AspectJ代替Spring AOP
6.3 非public方法
限制:默认情况下,Spring AOP只代理public方法:
- JDK动态代理只代理接口方法(通常是public)
- CGLIB可以代理protected和默认访问级别的方法,但需要特殊配置
解决方案:
将方法修改为public
配置CGLIB代理protected和默认访问级别方法:
java@EnableAspectJAutoProxy(proxyTargetClass = true) @Configuration public class AppConfig { // ... }
使用AspectJ代替Spring AOP
6.4 代理嵌套导致的性能问题
限制:多个切面应用到同一个bean时,可能会创建代理的嵌套链,影响性能。
解决方案:
- Spring自动优化:Spring会尝试将多个切面合并到一个代理中
- 减少切面数量:合并功能相似的切面
- 使用AspectJ编译时织入:避免运行时代理开销
6.5 内部不可见的类型
限制:AOP代理把参数和返回值暴露为接口类型而非实现类型,可能导致类型转换问题。
解决方案:
- 编程时使用接口类型,避免强制类型转换
- 使用引入(Introduction)添加额外的接口
- 配置
proxyTargetClass=true
使用CGLIB代理
7. Spring AOP与AspectJ的内部原理对比
Spring AOP和AspectJ都是实现AOP的工具,但它们的内部实现原理有很大区别。
7.1 织入时机
- Spring AOP:运行时织入,在程序运行时动态创建代理对象
- AspectJ:支持多种织入时机
- 编译时织入(Compile-Time Weaving):在编译源代码时织入
- 编译后织入(Post-Compile Weaving):在编译后但运行前织入
- 加载时织入(Load-Time Weaving):在类加载时织入
7.2 实现技术
- Spring AOP:基于代理的AOP实现
- 使用JDK动态代理或CGLIB创建代理
- 不修改原始类的字节码
- AspectJ:基于字节码修改的AOP实现
- 直接修改类的字节码
- 使用特殊的编译器或类加载时织入代理
7.3 功能对比
特性 | Spring AOP | AspectJ |
---|---|---|
连接点类型 | 仅方法执行 | 方法调用、字段访问、构造器、异常处理等 |
切点表达式 | 支持AspectJ表达式子集 | 完整的AspectJ表达式 |
性能 | 较低(运行时代理) | 较高(编译时织入几乎没有运行时开销) |
易用性 | 简单(集成在Spring中) | 复杂(需要额外的编译器或LTW配置) |
字节码修改 | 不修改 | 直接修改 |
织入非Spring Bean | 不支持 | 支持 |
自调用问题 | 存在 | 不存在 |
7.4 适用场景
Spring AOP适合:
- Spring项目中的简单横切关注点
- 方法级别的AOP需求
- 不需要修改框架或第三方库代码
AspectJ适合:
- 需要非方法连接点(如字段访问)
- 需要拦截非public方法或final类
- 需要拦截自调用
- 对性能要求极高的场景
- 需要拦截未由Spring管理的对象
8. 源码跟踪:AOP代理创建
为了更深入地理解Spring AOP的实现原理,我们来跟踪一下创建AOP代理的关键源码流程。
8.1 代理创建的入口
Spring AOP代理创建的主要入口是AbstractAutoProxyCreator.postProcessAfterInitialization()
方法:
java
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 如果需要,创建代理
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
8.2 判断是否需要创建代理
在wrapIfNecessary
方法中,Spring会检查是否需要为bean创建代理:
java
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 跳过已处理的bean
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 跳过不需要代理的bean(如AOP基础设施类)
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 查找适用于此bean的通知器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
8.3 查找适用的通知器
AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean()
方法负责查找适用于特定bean的通知器:
java
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 查找所有Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 筛选适用于此bean的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 对Advisor进行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
8.4 创建代理
AbstractAutoProxyCreator.createProxy()
方法负责创建代理对象:
java
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
// 创建ProxyFactory
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 决定是否使用CGLIB
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 检查是否有接口
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 构建Advisor链
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
// 控制是否冻结代理,冻结后无法添加新的通知
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 获取代理
return proxyFactory.getProxy(getProxyClassLoader());
}
8.5 选择代理方式并创建代理
DefaultAopProxyFactory.createAopProxy()
方法负责选择使用JDK动态代理还是CGLIB:
java
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 判断是否使用CGLIB
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果目标类是接口或已经是代理,使用JDK动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否则使用CGLIB
return new ObjenesisCglibAopProxy(config);
}
else {
// 默认使用JDK动态代理
return new JdkDynamicAopProxy(config);
}
}
9. 源码分析:AOP代理方法调用
当我们调用一个被Spring AOP代理的方法时,调用会被拦截并执行通知链。下面我们来看一下这个过程的源码实现。
9.1 JDK动态代理调用
当使用JDK动态代理时,所有方法调用都会被路由到JdkDynamicAopProxy.invoke()
方法:
java
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.getTargetSource();
Class<?> targetClass = null;
Object target = null;
try {
// 跳过Object类的方法,如equals()、hashCode()等
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// 实现代理对象的equals方法
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// 实现代理对象的hashCode方法
return hashCode();
}
// ...其他特殊方法处理...
// 设置暴露代理
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 获取目标对象
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 获取当前方法的拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 如果没有拦截器,直接调用目标方法
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 创建方法调用对象
MethodInvocation invocation = new ReflectiveMethodInvocation(
proxy, target, method, args, targetClass, chain);
// 执行拦截器链
retVal = invocation.proceed();
}
// 处理返回值类型
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType != Object.class
&& returnType.isInstance(proxy)) {
retVal = proxy;
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
9.2 CGLIB代理调用
CGLIB代理通过DynamicAdvisedInterceptor.intercept()
方法拦截方法调用:
java
@Override
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 {
// 与JDK代理类似的逻辑...
// 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 如果没有拦截器,直接调用目标方法
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// 创建方法调用对象
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
// 处理返回值...
return retVal;
}
finally {
// 清理资源...
}
}
9.3 拦截器链的执行
拦截器链的执行是通过ReflectiveMethodInvocation.proceed()
方法完成的:
java
@Override
public Object proceed() throws Throwable {
// 如果已经执行完所有拦截器,调用目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 获取下一个拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 处理动态拦截器
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// 跳过这个拦截器,继续下一个
return proceed();
}
}
else {
// 调用普通拦截器
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
9.4 常见通知类型的拦截器实现
每种通知类型都会被转换为相应的拦截器:
前置通知拦截器:
MethodBeforeAdviceInterceptor
java@Override public Object invoke(MethodInvocation mi) throws Throwable { // 在目标方法执行前执行通知 this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); // 继续执行拦截器链 return mi.proceed(); }
后置通知拦截器:
AfterReturningAdviceInterceptor
java@Override public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); // 在目标方法正常返回后执行通知 this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }
异常通知拦截器:
ThrowsAdviceInterceptor
java@Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } catch (Throwable ex) { // 查找匹配异常类型的通知方法 Method handlerMethod = getExceptionHandler(ex); if (handlerMethod != null) { // 调用异常通知 invokeHandlerMethod(mi, ex, handlerMethod); } throw ex; } }
环绕通知拦截器:直接使用用户提供的
MethodInterceptor
java@Override public Object invoke(MethodInvocation mi) throws Throwable { // 用户实现的环绕通知逻辑,可以控制是否调用mi.proceed() // 例如: try { // 前置逻辑 Object result = mi.proceed(); // 后置逻辑 return result; } catch (Exception ex) { // 异常处理逻辑 throw ex; } }
10. 最佳实践总结
基于对Spring AOP内部原理的深入理解,我们可以总结出以下最佳实践建议:
10.1 性能考虑
- 避免过度使用AOP:AOP虽然强大,但会带来额外的运行时开销
- 合理使用切点表达式:过于宽泛的切点表达式可能导致不必要的代理
- 对性能关键的应用考虑使用AspectJ:编译时织入几乎没有运行时开销
- 减少通知的嵌套层次:每一层通知都会增加方法调用的开销
10.2 代理选择
- 基于接口的设计:遵循面向接口编程的原则,使JDK动态代理更有效
- 明确选择代理方式:根据需要显式设置
proxyTargetClass
属性 - 注意CGLIB的限制:使用CGLIB时注意final方法和类的限制
10.3 切面设计
- 保持切面的单一职责:一个切面应该只关注一个横切关注点
- 正确使用通知类型:根据需求选择合适的通知类型
- 合理设置切面优先级:使用
@Order
或Ordered
接口控制切面执行顺序 - 避免在通知中执行耗时操作:特别是在前置通知和环绕通知中
10.4 自调用处理
- 意识到自调用问题:了解内部方法调用不会触发AOP通知
- 使用推荐的解决方案:自注入代理、使用AopContext或拆分类
10.5 异常处理
- 合理使用异常通知:在异常通知中不要捕获您不能处理的异常
- 注意异常转换:环绕通知可能会改变原始异常,影响异常通知
- 合理处理通知中的异常:通知本身抛出的异常会覆盖原始方法的异常
10.6 测试
- 测试代理行为:验证AOP代理如预期工作
- 模拟AOP环境:在单元测试中模拟AOP代理
- 测试边界情况:如内部调用、异常处理等
总结
本文深入探讨了Spring AOP的内部实现原理,从代理创建机制、切点表达式解析、拦截器链执行到源码分析,全面解析了Spring AOP的工作原理。
理解Spring AOP的内部原理不仅有助于我们更有效地使用AOP,还能帮助我们在遇到问题时更快地找到解决方案。虽然Spring AOP有一些固有的限制,但通过合理的设计和实践,我们可以充分发挥AOP的强大功能,构建更加模块化、可维护的应用系统。