一、AOP 是什么
AOP(Aspect Oriented Programming),即面向切面编程,是软件开发中一种告急的编程范式。它通过横向抽取机制,将那些与业务逻辑本身无关、却为业务模块所共同调用的逻辑或责任(如变乱处理处罚、日记管理、权限校验等)封装起来,然后通过“动态植入”的方式嵌入到业务逻辑的指定位置,从而实现业务逻辑的隔离与解耦。
AOP 是面向对象编程(OOP)的补充。在 OOP 中,我们通过类和继承来构造代码,但在某些环境下,会碰到一些题目。比方,当须要为多个不具有继承关系的对象添加公共方法时,如日记记载、性能监控 等,如果接纳 OOP 的方式,就须要在每个对象中都添加类似的方法,这会导致大量的重复代码,增长维护本钱。而 AOP 则可以很好地办理这个题目,它将这些公共逻辑抽取出来,会合管理,制止了重复代码的产生。
二、AOP 的上风
- 淘汰重复代码:将公共逻辑会合到切面中,制止了在多个地方重复编写类似的代码。
- 进步开发效率:开发者可以专注于业务逻辑的实现,而无需在每个地方都处理处罚那些公共的、与业务逻辑无关的逻辑。
- 方便维护:当须要修改公共逻辑时,只需修改切面中的代码,而无需修改每个利用该逻辑的地方。
三、AOP 的技能要点
(一)关照(Advice)
关照定义了“什么时间”和“做什么”。它包罗了须要用于多个应用对象的横切活动。根据关照的实行机会,可以分为以下几种类型:
- 前置关照(@Before):在目的方法调用之前调用关照。
- 后置关照(@After):在目的方法完成之后调用关照。
- 围绕关照(@Around):在被关照的方法调用之前和调用之后实行自定义的方法。须要注意的是,目的对象的方法须要手动实行。
- 返回关照(@AfterReturning):在目的方法乐成实行之后调用关照。
- 非常关照(@AfterThrowing):在目的方法抛出非常之后调用关照。
(二)毗连点(Join Point)
毗连点是程序实行过程中能够应用关照的所有点。在 Spring 中,毗连点指的是方法,由于 Spring 只支持方法类型的毗连点。
(三)切点(Pointcut)
切点定义了在“什么地方”举行切入,哪些毗连点会得到关照。切点表达式用于明确指定方法的返回类型、类名、方法名和参数名等与方法干系的部件。常用的切点表达式格式为:execution([修饰符] 返回值类型 包名.类名.方法名(参数))。此中,修饰符可以省略,返回值类型、包名、类名、方法名和参数都可以利用通配符(* 或 ..)来表示。
(四)切面(Aspect)
切面是关照和切点的团结。关照和切点共同定义了切面的全部内容——是什么、何时、何地完乐成能。
(五)引入(Introduction)
引入允许我们向现有的类中添加新方法大概属性。
(六)织入(Weaving)
织入是把切面应用到目的对象并创建新的署理对象的过程。织入分为编译期织入、类加载期织入和运行期织入。
四、AOP的底层原理
一、AOP 的底层原理概述
在 Spring 框架中,AOP 的实现依赖于动态署理技能。动态署理技能允许在运行时动态地创建署理对象,并在署理对象上调用方法时插入额外的逻辑(即关照)。Spring AOP 告急利用两种动态署理技能:JDK 动态署理和 CGLIB 署理。
二、JDK 动态署理技能
JDK 动态署理是 Java 提供的一种标准署理机制,它依赖于 Java 的反射机制。JDK 动态署理的核心是 java.lang.reflect.Proxy 类和 InvocationHandler 接口。以下是 JDK 动态署理的实现步调:
(一)为接口创建署理类的字节码文件
- 定义接口:起首,须要定义一个接口,目的对象和署理对象都将实现这个接口。比方:
- public interface UserService {
- void save();
- }
复制代码 - 实现目的类:目的类实现了上述接口,并提供了具体的业务逻辑。比方:
- public class UserServiceImpl implements UserService {
- @Override
- public void save() {
- System.out.println("业务层:保存用户...");
- }
- }
复制代码 - 创建署理类:通过 java.lang.reflect.Proxy 类动态天生署理类。署理类实现了与目的类类似的接口,并在方法调用时插入额外的逻辑。比方:
- 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("前置通知:记录日志
"); - // 执行目标方法
- Object result = method.invoke(target, args);
- // 在目标方法执行后插入逻辑
- System.out.println("后置通知:记录日志
"); - return result;
- }
- }
复制代码 - 天生署理实例:通过 Proxy.newProxyInstance 方法动态天生署理实例。比方:
- UserService userService = (UserService) Proxy.newProxyInstance(
- UserService.class.getClassLoader(), // 目标类的类加载器
- new Class<?>[]{UserService.class}, // 目标类实现的接口
- new MyInvocationHandler(new UserServiceImpl()) // 自定义的 InvocationHandler
- );
复制代码 (二)利用 ClassLoader 将字节码文件加载到 JVM
- 类加载器的作用:ClassLoader 负责加载字节码文件到 JVM 中。在 JDK 动态署理中,Proxy.newProxyInstance 方法会利用目的类的类加载器来加载天生的署理类。
- 动态天生字节码:Proxy 类会在运行时动态天生署理类的字节码,并通过类加载器加载到 JVM 中。署理类的字节码是基于目的类实现的接口动态天生的。
(三)创建署理类实例对象,实行对象的目的方法
- 署理类实例:通过 Proxy.newProxyInstance 方法天生的署理类实例对象,可以像普通对象一样调用接口方法。
- 方法调用:当调用署理类实例的方法时,现实上会调用 InvocationHandler 的 invoke 方法。在 invoke 方法中,可以插入前置关照、后置关照等逻辑,并终极调用目的方法。
三、CGLIB 署理技能
CGLIB(Code Generation Library)是一个强盛的字节码天生库,它可以在运行时动态天生目的类的子类,并覆盖目的类的方法。CGLIB 署理告急用于那些没有实现接口的类,大概须要署理类的方法而不是接口的方法。
(一)CGLIB 署理的基本原理
- 动态天生子类:CGLIB 通过字节码操作库(如 ASM)动态天生目的类的子类。天生的子类继承了目的类,并覆盖了目的类的方法。
- 方法拦截:在覆盖的方法中,CGLIB 提供了一个 MethodInterceptor 接口,用于拦截方法调用。在拦截器中,可以插入额外的逻辑,并终极调用目的方法。
(二)CGLIB 署理的实现步调
- 定义目的类:目的类不须要实现接口,比方:
- public class UserServiceImpl {
- public void save() {
- System.out.println("业务层:保存用户...");
- }
- }
复制代码 - 创建拦截器:实现 MethodInterceptor 接口,定义拦截逻辑。比方:
- public class MyMethodInterceptor 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 enhancer = new Enhancer();
- enhancer.setSuperclass(UserServiceImpl.class); // 设置目标类
- enhancer.setCallback(new MyMethodInterceptor()); // 设置拦截器
- UserServiceImpl userService = (UserServiceImpl) enhancer.create();
复制代码 - 利用署理实例:通过署理实例调用方法时,现实上会调用拦截器的 intercept 方法,在此中插入额外的逻辑,并终极调用目的方法。
四、JDK 动态署理与 CGLIB 署理的比力
表格
特性JDK 动态署理CGLIB 署理实用场景目的类必须实现接口目的类可以没有接口署理方式通过接口署理通过天生子类署理性能相对较好,由于基于接口调用稍差,由于须要天生子类并覆盖方法机动性只能署理接口方法可以署理类的方法,更机动实现机制基于 Java 反射和 InvocationHandler基于字节码操作库(如 ASM)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |