引言
在现代 Java 开发中,面向切面编程(AOP)已经成为解决横切关注点的主流方案。作为 Spring 框架的核心模块之一,Spring AOP 通过代理机制实现了强大的切面功能。本文将全面剖析 Spring AOP 的工作原理,深入讲解两种代理机制的实现细节,并补充实际开发中的最佳实践。
一、AOP 基础概念回顾
1.1 什么是 AOP
面向切面编程(Aspect-Oriented Programming)是一种通过预编译方式和运行期动态代理实现程序功能统一维护的技术。它是对 OOP 的补充,专门用于处理分布在应用中多处的功能(称为横切关注点)。
核心价值:
分离业务逻辑与系统服务(如日志、事务)
提高代码复用性
使开发者更专注于业务实现
1.2 AOP 核心术语
二、Spring AOP 代理机制深度解析
2.1 代理模式基础
代理模式是一种结构型设计模式,Spring AOP 基于代理模式实现,主要采用两种技术:
JDK 动态代理
CGLIB 代理
基于子类继承
通过修改字节码实现
不需要接口支持
无法代理 final 类和方法
2.2 JDK 动态代理实现详解
实现原理:
public class JdkProxyDemo {
interface Service {
void serve();
}
static class RealService implements Service {
public void serve() {
System.out.println("实际服务执行");
}
}
static class JdkProxyHandler implements InvocationHandler {
private final Object target;
public JdkProxyHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK代理】前置处理");
Object result = method.invoke(target, args);
System.out.println("【JDK代理】后置处理");
return result;
}
}
public static void main(String[] args) {
Service realService = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[]{Service.class},
new JdkProxyHandler(realService));
proxy.serve();
}
}
复制代码
关键点分析:
通过Proxy.newProxyInstance
创建代理实例
InvocationHandler
负责拦截所有方法调用
代理对象会实现目标接口的所有方法
2.3 CGLIB 代理实现详解
实现原理:
public class CglibProxyDemo {
static class RealService {
public void serve() {
System.out.println("实际服务执行");
}
}
static class CglibInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("【CGLIB代理】前置处理");
Object result = proxy.invokeSuper(obj, args);
System.out.println("【CGLIB代理】后置处理");
return result;
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealService.class);
enhancer.setCallback(new CglibInterceptor());
RealService proxy = (RealService) enhancer.create();
proxy.serve();
}
}
复制代码
关键点分析:
使用Enhancer
创建代理类
通过setSuperclass
指定目标类
MethodInterceptor
处理所有方法调用
生成的目标类子类字节码
2.4 两种代理对比
三、Spring AOP 工作原理补充
3.1 代理创建流程
Bean 初始化阶段:在AbstractAutowireCapableBeanFactory
中完成
代理判断:通过AbstractAutoProxyCreator
检查是否需要代理
通知获取:收集所有适用的 Advisor
代理生成:根据配置选择 JDK 或 CGLIB 方式
代理缓存:生成的代理对象会被缓存复用
3.2 方法调用链
Spring AOP 使用责任链模式处理拦截器调用:
客户端调用 → 代理对象 → 拦截器链 → 目标方法
复制代码
核心实现类ReflectiveMethodInvocation
负责维护和执行拦截器链。
3.3 性能优化要点
1、切点表达式优化:
2、代理选择策略:
// 强制使用CGLIB代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
复制代码
3、缓存利用:
Spring 默认会缓存代理类和切点匹配结果
避免在切面中频繁创建新对象
四、高级特性与最佳实践
4.1 解决自调用问题
问题场景:
@Service
public class OrderService {
public void placeOrder() {
this.validate(); // 自调用不会触发AOP
}
@Transactional
public void validate() {
// 事务不会生效
}
}
复制代码
解决方案:
1、重构代码结构,避免自调用
2、通过 AopContext 获取当前代理:
((OrderService) AopContext.currentProxy()).validate();
复制代码
3、使用 AspectJ 编译时织入
4.2 动态切面配置
Spring 允许运行时修改切面配置:
Advised advised = (Advised) applicationContext.getBean("serviceBean");
advised.addAdvice(new MyNewAdvice());
advised.removeAdvice(oldAdvice);
复制代码
4.3 引入(Introduction)
为对象动态添加接口实现:
@Aspect
public class IntroductionAspect {
@DeclareParents(value="com.example.service.*",
defaultImpl=DefaultLockable.class)
public static Lockable mixin;
}
复制代码
五、Spring AOP 与 AspectJ 对比
选型建议:
六、常见问题排查
1、代理不生效检查清单:
确保目标 Bean 由 Spring 管理
检查切点表达式是否匹配
确认方法调用是通过代理对象
检查是否有多个代理互相覆盖
2、代理类型检查工具:
AopUtils.isAopProxy(bean); // 是否代理对象
AopUtils.isCglibProxy(bean); // 是否CGLIB代理
AopUtils.isJdkDynamicProxy(bean);// 是否JDK代理
复制代码
3、获取原始目标对象:
if (AopUtils.isAopProxy(bean)) {
Object target = ((Advised) bean).getTargetSource().getTarget();
}
复制代码
结语
Spring AOP 通过巧妙的代理机制实现了强大的切面编程能力。理解其底层原理对于正确使用和问题排查至关重要。在实际项目中,建议:
根据具体场景选择合适的代理方式
遵循"单一职责"原则设计切面
注意性能敏感场景的优化
合理利用 Spring 的调试工具进行问题诊断
希望本文能帮助你深入理解 Spring AOP 的代理机制,在项目中更加得心应手地使用 AOP 解决横切关注点问题。
文章转载自:佛祖让我来巡山
原文链接:https://www.cnblogs.com/sun-10387834/p/18932382
体验地址:http://www.jnpfsoft.com/?from=001YH
评论