切面编程(Aspect-Oriented Programming,AOP)是一种软件开发方法,旨在通过分离关注点(Concerns)来增强代码的模块性、可维护性和可重用性。
AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
切面编程通过将交叉关注点(Cross-Cutting Concerns)从主要业务逻辑中抽离出来,从而使代码更易于管理和理解。
作为一个Java工程师,你可能会经常遇到需要处理跨越多个模块和类的问题,例如日志记录、事务管理、安全认证等。这些问题通常与主要的业务逻辑分离,但它们又贯穿在整个应用中。这就是切面编程的用武之地。
以下是切面编程的关键概念和组成部分:
切面编程的优势在于它提供了一种清晰的方式来处理横切关注点,将其与主要业务逻辑分离,从而增强代码的可维护性和可扩展性。在 Java 中,切面编程的实现主要通过 Spring AOP 框架来实现,它允许你通过注解或 XML 配置来创建和管理切面。使用切面编程,你可以更加专注于核心业务逻辑,而无需过多关心交叉关注点的处理。
AOP(Aspect-Oriented Programming,面向切面编程)的作用是通过分离交叉关注点(Cross-Cutting Concerns)来提高代码的模块性、可维护性和可重用性。它允许开发人员将横切关注点(如日志、事务、安全性等)从主要业务逻辑中分离出来,使代码更加清晰、可维护和可扩展。
AOP 的优势体现在以下几个方面:
总而言之,作用及其优势为:
AOP 的底层实现通常基于动态代理和字节码操作,以在运行时将切面逻辑织入到目标代码中。
在 Java 中,主要有两种常见的 AOP 底层实现方式:基于 JDK 动态代理和基于 CGLIB 字节码操作。
下面分别介绍这两种实现方式的基本原理:
AOP 的底层实现大致流程如下:
需要注意的是,虽然 AOP 通过代理技术实现了切面逻辑的织入,但这种织入是在运行时动态完成的,因此可能会带来一些性能开销。选择使用 JDK 动态代理还是 CGLIB 字节码操作取决于目标类是否实现接口等因素。
总之,AOP 的底层实现主要基于动态代理和字节码操作,通过在运行时将切面逻辑织入到目标代码中,实现了交叉关注点的分离和处理。
目录如下:
目标接口:
目标对象:
增强方法:
代理工具:
package com.xzl.proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author 逐梦苍穹 * @date 2023/8/13 10:28 */ public class ProxyUtil { public static Advice advice = new Advice(); public staticT getProxy(T object) { return (T) Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { advice.before(); Object result = method.invoke(object, objects); advice.afterReturning(); return result; } }); } }
测试方法:
测试结果:
CGLIB(Code Generation Library)是一个功能强大的字节码操作库,用于在运行时生成和修改 Java 类的字节码。CGLIB 动态代理是一种基于字节码操作的动态代理机制,与 JDK 动态代理不同,它可以代理类而不仅仅是接口,适用于那些没有实现接口的类。
CGLIB 被广泛应用于许多 Java 框架和库,其中包括 Spring AOP。
以下是 CGLIB 动态代理的基本原理和使用方式:
目录如下:
增强方法:
目标对象:
代理工具:
package com.xzl.proxy.cglib; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @author 逐梦苍穹 * @date 2023/8/13 10:28 */ public class ProxyUtil{ //创建增强方法 public static Advice advice = new Advice(); //创建目标对象 public final T target; //初始化目标对象 public ProxyUtil(T target){ this.target = target; } public T getProxy(T t){ Enhancer enhancer = new Enhancer();//创建增强器 enhancer.setSuperclass(t.getClass());//设置父类 //设置回调 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { advice.before(); Object invoke = method.invoke(target,objects); advice.afterReturning(); return invoke; } }); //创建代理对象并返回 return (T) enhancer.create(); } }
测试:
结果:
两种代码实现方式的区别有:
区别解释:
根据你的需求和场景,可以选择适合的动态代理方式。如果目标类实现了接口,可以考虑使用 JDK 动态代理;如果目标类没有实现接口,可以考虑使用 CGLIB 动态代理。