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. Go的接口与多态

    什么是Go的接口? 接口可以说是一种类型,可以粗略的理解为他的变量是一堆方法. 一个简单的案例: r,_ := http.Get("http://www.baidu.com") i ...

  2. test(爱测试) 开源接口测试,敏捷测试管理平台10.2.7发布

    一:itest work 简介 itest work 开源敏捷测试管理,包含极简的任务管理,测试管理,缺陷管理,测试环境管理,接口测试,接口Mock,还有压测 ,又有丰富的统计分析,8合1工作站.可按 ...

  3. uniapp 页面跳转传值和接收

    前端面试题库地址:https://www.yuque.com/sxd_panda/sdluga 1.首先介绍最原始的跳转方法,类似于html中的a标签,不过在uniapp中需要将a标签换成 <n ...

  4. minos 0 前(废)言(话)

    - 首发公号:Rand_cs minos 0 前(废)言(话) 从今天开始开启一个新的系列,讲述虚拟化的那些事儿.时隔上次发文又隔了好几个月了,主要是平时工作比较忙,没太多时间精力维护博客之类的. 前 ...

  5. ETL工具-nifi干货系列 第十八讲 nifi Funnel实战教程

    1.Funnel (漏斗),Funnel是 NiFi 组件,用于将多个连接中的数据合并到一个连接中. 使用场景:nifi中的Funnel组件用于合并多个数据流并将它们传递到下游处理器.它可以将来自不同 ...

  6. Apache Kylin(三)Kylin上手

    Kylin 上手 根据Kylin 官方给出的测试数据,我们实际操作一下 Kylin. 1. 导入 Hive 数据 首先创建一个project,在界面左上角有个"Add Project&quo ...

  7. Linux 内核:GPIO子系统(1)软件框架

    Linux 内核:GPIO子系统(1)软件框架 背景 在很多驱动开发中,GPIO用得很多,因此学习一下:也会顺便看看pinctrl 子系统. 原文(有删改):http://www.wowotech.n ...

  8. SNAT,DNAT以及REDIRECT转发详解

    最近负责的其中一个项目的服务器集群出现了点网络方面的问题,在处理过程当中又涉及到了防火墙相关的知识和命令,想着有一段时间没有复习这部分内容了,于是借着此次机会复写了下顺便将本次复习的一些内容以博客的形 ...

  9. PHP接入苹果支付

    Ios苹果支付流程: 客户端先从苹果获取内购Id. 客户端将内购id,金额.用户id等传给服务端获取一个自己服务端生成的订单号. 客户端向苹果发起支付. 支付成功后,客户端从本地拿支付凭证.将支付凭证 ...

  10. 【资料分享】RK3568核心板规格书(4x ARM Cortex-A55(64bit),主频1.8GHz)

    1 核心板简介 创龙科技SOM-TL3568是一款基于瑞芯微RK3568J/RK3568B2处理器设计的四核ARM Cortex-A55全国产工业核心板,每核主频高达1.8GHz/2.0GHz.核心板 ...