Spring AOP(面向切面编程)

以下内容由ChatGPT生成

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过分离关注点来提高程序的模块化。Spring AOP 主要用于横切关注点(如日志记录、安全、事务管理等)的实现。在 Spring 中,AOP 的主要功能是为 Bean 增强功能,如添加额外的行为。

1. 静态代理与动态代理

静态代理动态代理是实现 AOP 的两种主要方式。

静态代理

  • 在编译时就已经知道代理的目标类,代理类在代码中显式地定义。
  • 静态代理的缺点是需要为每个代理的类手动编写代理类,导致代码冗余且难以维护。

动态代理

  • 动态代理是在运行时生成代理类的,Java 中有两种实现动态代理的方式:JDK 动态代理CGLIB
  • 动态代理的优点是可以为任意接口生成代理,不需要手动编写代理类。
JDK 动态代理
  • JDK 动态代理只代理实现了接口的类。它通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现。
  • InvocationHandler 接口中定义了 invoke 方法,当代理对象调用方法时,会执行该方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class JdkProxyExample {
public static void main(String[] args) {
Foo foo = new FooImpl();
Foo proxyFoo = (Foo) Proxy.newProxyInstance(
Foo.class.getClassLoader(),
new Class<?>[]{Foo.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
System.out.println("Before method: " + method.getName());
Object result = method.invoke(foo, args);
// 后置增强
System.out.println("After method: " + method.getName());
return result;
}
});
proxyFoo.doSomething();
}
} interface Foo {
void doSomething();
} class FooImpl implements Foo {
public void doSomething() {
System.out.println("Doing something...");
}
}
CGLIB 动态代理
  • CGLIB 动态代理通过生成目标类的子类来实现代理,因此可以代理没有接口的类。CGLIB 使用 ASM 字节码操作库来生成代理类。
  • CGLIB 的代理类重写目标类的方法,通过调用父类的 super 方法来实现对目标方法的调用。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; public class CglibProxyExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Foo.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 前置增强
System.out.println("Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
// 后置增强
System.out.println("After method: " + method.getName());
return result;
}
});
Foo fooProxy = (Foo) enhancer.create();
fooProxy.doSomething();
}
} class Foo {
public void doSomething() {
System.out.println("Doing something...");
}
}

2. Spring AOP 实现原理

Spring AOP 支持 JDK 动态代理CGLIB 两种代理机制。

  • JDK 动态代理:当目标类实现了一个或多个接口时,Spring 默认使用 JDK 动态代理来为目标类创建代理对象。
  • CGLIB:如果目标类没有实现任何接口,Spring 则会使用 CGLIB 来生成目标类的代理对象。

Spring 使用 AopProxy 接口和其两个实现类 JdkDynamicAopProxyCglibAopProxy 来分别处理这两种代理机制。

Bean 被包装成 Proxy
  1. Spring 容器启动时,解析配置文件或注解,生成 Bean 定义信息。
  2. 在 Bean 初始化后,Spring AOP 的 BeanPostProcessor 之一(如 AbstractAutoProxyCreator 的子类)会检查该 Bean 是否需要 AOP 增强。
  3. 如果需要增强,则会生成一个代理对象,替换掉原始的 Bean。这一过程是通过调用 getProxy() 方法来完成的。
创建 Proxy 对象

AopProxy 接口定义了 getProxy() 方法:

  • JdkDynamicAopProxy:通过 JDK 动态代理的 Proxy.newProxyInstance() 方法创建代理对象。
  • CglibAopProxy:通过 CGLIB 的 Enhancer 类创建代理对象。
获取代理对象

getProxy() 方法返回代理对象。代理对象的创建是在调用 getProxy() 方法时动态生成的,并且在这个方法中处理了所有的 AOP 增强逻辑。

InvocationHandler 的实现

在 JDK 动态代理中,InvocationHandlerinvoke() 方法包含了拦截器链的逻辑。CglibAopProxy 通过 CallbackMethodInterceptor 实现类似的功能。

public class MyInvocationHandler implements InvocationHandler {
private final Object target; public MyInvocationHandler(Object target) {
this.target = target;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置处理
System.out.println("Before method: " + method.getName()); // 调用目标对象的方法
Object result = method.invoke(target, args); // 后置处理
System.out.println("After method: " + method.getName()); return result;
}
}

invoke() 方法中:

  1. 执行前置增强逻辑。
  2. 使用反射调用目标对象的方法。
  3. 执行后置增强逻辑。

3. 拦截器链与方法链的执行

Spring AOP 中的拦截器链是由 AdvisorMethodInterceptor 组成的。Advisor 包含切点(Pointcut)和通知(Advice),切点定义了哪些方法需要拦截,通知则定义了拦截时执行的逻辑。

在代理对象调用方法时:

  1. AopProxy 调用链会依次调用拦截器链中的拦截器。
  2. 拦截器链通过责任链模式处理每一个拦截器。
  3. 如果拦截器链中的某个拦截器决定执行目标方法,则会调用 MethodInvocation.proceed() 方法。
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 前置处理
System.out.println("Before method: " + invocation.getMethod().getName()); // 调用目标对象的方法
Object result = invocation.proceed(); // 后置处理
System.out.println("After method: " + invocation.getMethod().getName()); return result;
}
}

invoke() 方法中,proceed() 方法用于继续执行下一个拦截器或目标方法。如果没有其他拦截器,则执行目标方法。

总结

Spring AOP 使用代理模式实现横切关注点的管理,主要通过 JDK 动态代理和 CGLIB 动态代理实现。代理对象是通过 AopProxy 创建的,其中的 InvocationHandlerMethodInterceptor 负责执行拦截器链的逻辑。Spring AOP 提供了强大的功能来增强 Bean 的行为,使得切面逻辑与核心业务逻辑分离,提升了代码的模块化和可维护性。

Spring AOP概念及原理的更多相关文章

  1. Spring aop的实现原理

    简介 前段时间写的java设计模式--代理模式,最近在看Spring Aop的时候,觉得于代理模式应该有密切的联系,于是决定了解下Spring Aop的实现原理. 说起AOP就不得不说下OOP了,OO ...

  2. Spring AOP 的实现 原理

    反射实现 AOP 动态代理模式实例说明(Spring AOP 的实现 原理)   比如说,我们现在要开发的一个应用里面有很多的业务方法,但是,我们现在要对这个方法的执行做全面监控,或部分监控.也许我们 ...

  3. Spring AOP异常捕获原理

    Spring AOP异常捕获原理:        被拦截的方法,须显式的抛出异常,且不能做任何处理, 这样AOP才能捕获到方法中的异常,进而进行回滚.        换句话说,就是在Service层的 ...

  4. 漫画 | Spring AOP的底层原理是什么?

    1.Spring中配置的bean是在什么时候实例化的? 2.描述一下Spring中的IOC.AOP和DI IOC和AOP是Spring的两大核心思想 3.谈谈IOC.AOP和DI在项目开发中的应用场景 ...

  5. Spring AOP动态代理原理与实现方式

    AOP:面向切面.面向方面.面向接口是一种横切技术横切技术运用:1.事务管理: (1)数据库事务:(2)编程事务(3)声明事物:Spring AOP-->声明事物   2.日志处理:3.安全验证 ...

  6. Spring AOP概念及作用

    一:SpringAOP概念 面向切面编程(Aspect Oriented Programming)提高了另一种角度来思考程序的结构,通过预编译方式和运行期间的动态代理实现程序功能的统一维护的一种技术. ...

  7. Spring技术内幕:Spring AOP的实现原理(二)

    **二.AOP的设计与实现 1.JVM的动态代理特性** 在Spring AOP实现中, 使用的核心技术时动态代理.而这样的动态代理实际上是JDK的一个特性.通过JDK的动态代理特性,能够为随意Jav ...

  8. Spring技术内幕:Spring AOP的实现原理(一)

    一.SpringAOP的概述 1.AOP概念 AOP是Aspect-Oriented Programming(面向切面编程)的简称.维基百科的解释例如以下: Aspect是一种新的模块化机制,用来描写 ...

  9. jdk动态代理与cglib代理、spring aop代理实现原理

    原创声明:本博客来源与本人另一博客[http://blog.csdn.net/liaohaojian/article/details/63683317]原创作品,绝非他处摘取 代理(proxy)的定义 ...

  10. jdk动态代理与cglib代理、spring aop代理实现原理解析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

随机推荐

  1. JS注释 JS变量

    <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8 ...

  2. Ubuntu 上使能 SELinux

    首发公号:Rand_cs 此文档说明如何在 ubuntu 上启用 SELinux,测试环境为虚拟机,开始前一定一定一定先来个快照,不要问我为什么有三个一定. 卸载 apparmor(可选) ubunt ...

  3. 程序员面试金典-面试题 16.20. T9键盘

    题目: 在老式手机上,用户通过数字键盘输入,手机将提供与这些数字相匹配的单词列表.每个数字映射到0至4个字母.给定一个数字序列,实现一个算法来返回匹配单词的列表.你会得到一张含有有效单词的列表.映射如 ...

  4. 通过 Helm Chart 部署 Easysearch

    Easysearch 可以通过 Helm 快速部署了,快来看看吧! Easysearch 的 Chart 仓库地址在这里 https://helm.infinilabs.com. 使用 Helm 部署 ...

  5. 使用 OpenTelemetry 构建可观测性 02 - 埋点

    这是讲解 OpenTelemetry 系列博客的第二篇.在上一篇博客中,我们介绍了 OpenTelemetry 是什么以及由什么组成.现在我们将讨论如何使用 OTel 准确收集遥测数据和链路追踪数据. ...

  6. mongodb连接类

    import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import com.mongodb.cl ...

  7. tempcode排序

    package com.hsy;import com.alibaba.fastjson.JSON;import org.springframework.util.CollectionUtils;imp ...

  8. Freertos学习:在Posix环境仿真FreeRTOS

    --- title: rtos-freertos-在Posix环境仿真FreeRTOS date: 2020-06-11 16:22:34 categories: tags: - freertos - ...

  9. BigCodeBench: 继 HumanEval 之后的新一代代码生成测试基准

    HumanEval 是一个用于评估大型语言模型 (LLM) 在代码生成任务中的参考基准,因为它使得对紧凑的函数级代码片段的评估变得容易.然而,关于其在评估 LLM 编程能力方面的有效性越来越多的担忧, ...

  10. FPGA案例开发手册——基于全志T3+Logos FPGA核心板

    前 言 本文档主要提供评估板FPGA端案例测试方法,适用的开发环境为Windows 7 64bit和Windows 10 64bit. 本文案例基于创龙科技的全志T3+Logos FPGA核心板,它是 ...