Spring AOP 实现原理

张开发
2026/6/7 19:57:31 15 分钟阅读
Spring AOP 实现原理
关于 Aspect 切面在 AOP 的使用中Aspect 切面由 Pointcut 和 Advice 组成但是没有写到如何进行切入的这就是本篇博客所介绍的“ Proxy ”即代理。Proxy 代理首先当在 bean 的生命周期中spring在给 bean 执行初始化后置即方法postProcessAfterInitialization()的时候一般情况下是这样如果是出现了bean循环依赖会在实例化结束后即调用了createBeanInstance()反射方法之后依赖注入之前进行生成 Proxy去进行 AOP 的生成。因为初始化后置方法是每生成一个 Bean 就要执行一次这个方法正好对应了 AOP 的需求即为每一个需要 AOP 的 Bean 进行面向切面编程。而就是这个时候 AOP 就会生成 Bean 的代理对象Proxy。关于Proxy的实现方式这里运用到了一个技术叫做“动态代理”。而对于动态代理的实现方式有两种分别为JDK 代理和CGLIB 代理。为了方便理解这里先用“静态代理”做演示。1. 静态代理定义接口首先先写一个接口。java// 定义接口类 UserServicepublic interface UserService {void saveUser(String username);}接口实现再写一个实现类去实现此接口。java// 接口实现类 UserServiceImplpublic class UserServiceImpl implements UserService {Overridepublic void saveUser(String username) {System.out.println(【真实业务】正在保存用户: username);}}静态代理类这里是静态代理的重点静态代理类创建。java// 静态代理类 UserServiceStaticProxypublic class UserServiceStaticProxy implements UserService {private UserService target;// 拿到目标对象public UserServiceStaticProxy(UserService target) {this.target target;}// 运用重写的操作进行业务增强Overridepublic void saveUser(String username) {// 前置增强System.out.println(静态代理-前置操作...);// 调用目标方法target.saveUser(username);// 后置增强System.out.println(静态代理-后置操作...);}}实际使用使用静态代理进行工作java// 实际的使用public class StaticProxyTest {public static void main(String[] args) {// 创建一个实现类为目标类 targetUserService target new UserServiceImpl();// 创建一个代理对象 proxy 进行对 target 的代理UserService proxy new UserServiceStaticProxy(target);// 执行代理的方法这是重写增强后的方法proxy.saveUser(张三);}}2. 动态代理 之 JDK代理定义接口同上写一个接口。java// 定义接口类 UserServicepublic interface UserService {void saveUser(String username);}接口实现也是写一个实现类去实现此接口。java// 接口实现类 UserServiceImplpublic class UserServiceImpl implements UserService {Overridepublic void saveUser(String username) {System.out.println(真实业务... username);}}动态代理重点java// 实现动态代理public class JdkProxyHandler implements InvocationHandler {private Object target;public Object bind(Object target) {this.target target;return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 前置增强System.out.println(JDK代理-前置方法调用: method.getName());// 核心通过反射调用目标方法Object result method.invoke(target, args);// 后置增强System.out.println(JDK代理-后置方法调用);// 返回原方法的执行结果return result;}}首先对于方法 invoke() 这是一种特殊的方法每当调用代理 proxy 的时候proxy 会自动执行invoke()方法而这也是所谓proxy代理的核心作用。invoke() 的参数 proxy 指的是本身 method 指的是原对象的方法 arg 表是以列表的形式存放的原对象的方法所需的参数。对于方法 Proxy.newProxyInstance 这里的 proxy 是一种 “伪造的实现类” 通过 getClassLoader() 和 getInterfaces() 将目标的 “类的加载” 和 “接口实现” 传给了proxy。并通过处理器 new JdkProxyHandler(target) 的处理从而使 proxy 成功 “伪装” 成了实现了对应接口的目标类变成了原方法的 “替身” 。实际使用java// 实际的使用public class JdkProxyTest {public static void main(String[] args) {JdkProxyHandler jdkProxyHandler new JdkProxyHandler();// 用接口类去接收这个 proxy 代理类UserService userService (UserService)jdkProxyHandler.bind(new UserServiceImpl())userService.saveUser(张三);}}对于代码 UserService userService (UserService)jdkProxyHandler.bind(new UserServiceImpl()) 它将原实现类 new UserServiceImpl() 通过 bind 方法进行包装成一个proxy代理类然后用接口类实例 UserService userService 接收。问题来了 那为什么不用原来的实现类接收而是用接口类接收呢答 proxy相当于这个接口的实现类而原目标类也是这个接口的实现类。虽然proxy是增强了目标类但是和目标类本质上不一样所以相当于现在是有两个实现类都实现了同一个接口所以才用接口接收以免报错。3. 动态代理 之 CGLIB代理定义接口CGLIB 其实不强依赖接口。java// 不需要接口哦普通类实现CGLIB的强大之处在于即使只有一个普通的类没有接口它也能进行代理。java体验AI代码助手代码解读复制代码// 普通类 UserServiceImpl (不需要 implements UserService 去实现接口)public class UserServiceImpl {public void saveUser(String username) {System.out.println(真实业务...正在保存用户: username);}}动态代理重点java// 实现动态代理public class CglibProxyHandler implements MethodInterceptor {// 创建代理对象的核心方法public Object bind(Class? targetClass) {// 1. 创建增强器代理工厂Enhancer enhancer new Enhancer();// 2. 设置目标类也就是父类enhancer.setSuperclass(targetClass);// 3. 设置回调也就是拦截器本身enhancer.setCallback(this);// 4. 创建并返回代理对象return enhancer.create();}Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {// 前置增强System.out.println(CGLIB代理-前置方法调用: method.getName());// 通过 MethodProxy 调用父类目标类的方法// 注意这里使用 invokeSuper 而不是 method.invoke效率更高Object result methodProxy.invokeSuper(proxy, args);// 后置增强System.out.println(CGLIB代理-后置方法调用);return result;}}关于 enhancer 这个所谓的 “增强器” 其实就是一个 “代理工厂”可以用 enhancer.create() 生成代理对象 proxy 。关于 enhancer.setCallback() 方法参数为 this 。这个方法叫做 “回调” 是为了将下面的 intercept 增强的方法传递给代理工厂 enhancer等到需要生成代理对象 proxy 的时候工厂会自动将这个 intercept 增强方法塞进代理对象 proxy 中。对于方法 enhancer.create()这里的 proxy 是一种 “伪造的子类” 即将目标类作为父类让代理类 proxy 去继承目标类。CGLIB 会在内存中动态生成了一个继承自目标类的新类 就是这个 proxy 。它通过重写父类的所有非 final 方法并在方法中织入我们的增强逻辑从而变成了原方法的“替身”。对于方法 intercept()这是 CGLIB 的拦截核心。每当调用代理对象的方法时会执行此方法。 proxy指的是CGLIB 生成的代理对象本身。 method指的是目标类的方法。 args指的是方法参数。 methodProxy这是 CGLIB 特有的参数它是对目标方法的快速代理比 JDK 的反射调用性能更好。实际使用java// 实际的使用public class CglibProxyTest {public static void main(String[] args) {CglibProxyHandler cglibProxyHandler new CglibProxyHandler();// 用原来的类去接收这个 proxy 代理类UserServiceImpl userService (UserServiceImpl) cglibProxyHandler.bind(UserServiceImpl.class);userService.saveUser(张三);}}对于代码 UserServiceImpl userService (UserServiceImpl) cglibProxyHandler.bind(UserServiceImpl.class)它将原类 UserServiceImpl.class 通过 bind 方法包装成一个 proxy 代理类然后用原类实例UserServiceImpl userService 接收。问题来了为什么 CGLIB 可以用原来的类接收而 JDK 代理不行呢答因为 CGLIB 采用的是继承的方式。CGLIB 生成的代理类是目标类的子类而目标类是父类。在 Java 中子类对象是可以赋值给父类引用的多态。所以代理对象子类可以直接被当作目标类父类来使用自然就可以用原来的类去接收了。关于动态代理使用在 Spring 框架中AOP 代理的创建是由 AopProxyFactory 决定的。Spring 默认遵循以下策略如果目标对象实现了接口默认使用 JDK 动态代理。建议在这种情况下业务类应当实现接口这是 Java 设计模式的最佳实践。如果目标对象没有实现接口必须使用 CGLIB。强制使用 CGLIB即使目标对象实现了接口你也可以通过配置强制 Spring 使用 CGLIB 例如在 XML 中设置 aop:config proxy-target-classtrue/在注解配置中开启 proxyTargetClasstrue 。注意使用 CGLIB 时目标类不能是 final 的且需要被代理的方法也不能是 final 或 private 的因为子类无法重写这些方法。

更多文章