定义
AOP (Aspect-Oriented Programming) : 面向切面编程,利用 AOP 可以对业务的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可用性,同时提高了效率;通俗来讲就是在不修改源代码的情况下,对源代码的功能进行增强。
面向切面编程(Aspect-oriented Programming 简称AOP)是
面向对象编程(Object-oriented Programming 简称OOP)的一种功能补充。
Aop 的一些术语。
| Join Point(连接点) | 连接点,这些连接点可以是方法或构造函数的调用和执行、异常的处理、字段分配和访问等。我们通常将类中的多个方法看做连接点。 | 
|---|---|
| Pointcuts(切入点) | 切入点,通常是一个表达式,是我们需要增强的地方。例如对象中有十个方法,其中add()方法被增强了,那么这个方法就是切入点。 | 
| Advice(增强/通知) | 实际增强的部分,比如记录日志、判断权限、事务管理等。增强有多个类型:前置增强、后置增强、环绕增强、异常增强、最终增强。在切入点执行增强。 | 
| Aspects(切面) | 由切点和增强组成,它的定义非常像一个类,除了横切成员之外,还可以有方法、字段和初始化器。那么将增强应用到切入点上的过程就是在处理切面。 | 
Aspectj
说动面相切面编程,那么就得说一下这个 AOP 框架 Aspectj !
Aspectj一个易用的、功能强大的 AOP  编程语言。它是一个独立的框架。
官网地址是:The AspectJ Project | The Eclipse Foundation 。
文档:AspectJ Documentation | The Eclipse Foundation
Spring AOP
再说一下 Spring AOP !
Spring AOP 使用了AspectJ 的 Annotation。使用 Aspect 来定义切面,使用 Pointcut 来定义切入点,使用 Advice 来定义增强处理。虽然使用了 Aspect 的 Annotation,但是并没有使用它的编译器和织入器。其实现原理是JDK 动态代理,在运行时生成代理类。
Spring 对 @AspectJ 方面配置的支持,需要在配置文件中添加如下配置:
<aop:aspectj-autoproxy/>
实现原理:
如果我们配置了AOP,那么 Spring 在创建相关的 bean 的时候,会给这个 bean 一个代理对象,我们后续对 bean 中方法的调用,实际上调用的是代理类重写的代理方法。
Spring的 AOP 使用了两种动态代理,分别是 JDK 的动态代理以及 CGLib 的动态代理:
- 被代理的类有接口 , 使用 JDK 的动态代理,创建接口实现类代理对象,增强类的方法
 - 被代理的类没有接口,使用 CGLIB 代理,创建子类的代理对象,增强类的方法
 
:::tips 在J AVA平台中,对于 AOP 增强,有三种方式:
- 编译期:编译器把切面的调用编译进字节码,这种方式需要扩展编译器;
 - 类加载器:目标被加载到 JVM,通过特殊的加载器,对目标字节码进行增强;
 - 运行期:目标对象和切面都是 JAVA 对象,通过 JVM 的动态代理或者第三方库运行期动态进行增强。(Spring 使用的这个) :::
 
代码测试实现:spring - aop
Spring AOP 和 AspectJ 的区别 ?
Spring AOP 和 AspectJ 都是实现 AOP 的框架,其中 Spring AOP 是基于动态代理实现,而 AspectJ 是基于字节码实现的 AOP 框架。
下面是Spring AOP 和 AspectJ 的一些区别:
| 性能 | Spring AOP 基于代理实现 AOP,相比之下,AspectJ 基于字节码操作实现 AOP,因此 AspectJ 比 Spring AOP 更快,但是Spring AOP 的性能也很不错。 | 
|---|---|
| 表达式 | Spring AOP 仅支持使用 AspectJ 风格的切点表达式,而 AspectJ 使用的是一种更强大的 AspectJ 切点定义语言,并且提供了更多的控制和灵活性。 | 
| 编程模型 | Spring AOP 使用声明式编程模型,因为它允许您使用注解或 XML 配置来定义切面和切点。使用 AspectJ 时,您必须在 Java 类中显式地定义切面和切点。 | 
| 可移植性 | Spring AOP 是 Spring 框架的一部分,因此它具有更好的可移植性。而 AspectJ 则需要独立使用,相对来说不太便于集成到其他框架中。 | 
综上 AspectJ 的灵活性和性能优势使它更适合复杂的 AOP 应用程序。
对于简单的 AOP 场景,Spring AOP 是一种更简单和更易于使用的框架。
JDK 代理
静态代理
准备测试目标接口
public interface UserService {void save(int a);void get();void delete(int id);}@Service("userService")public class UserServiceImpl implements UserService {public void save(int a) {System.out.println("save 方法执行");}public void get() {System.out.println("get 方法执行");}public void delete(int id) {System.out.println("delete 方法执行");}}
静态代理,纯手动
@Componentpublic class UserServiceProxy implements UserService {private final UserServiceImpl userService = new UserServiceImpl();@Overridepublic void save(int a) {System.out.println("proxy save method invoke");userService.save(a);}@Overridepublic void get() {}@Overridepublic void delete(int id) {}}
测试
@DisplayName("JDK静态代理")@Testpublic void jdkStaticProxy() {AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext("cn.lichenghao.aop");UserServiceProxy userServiceProxy = context.getBean(UserServiceProxy.class);userServiceProxy.save(1);}
优点简单;缺点也很明显需要给每个类手动设置代理,如果目标对象很多的话…..
动态代理
需要实现InvocationHandler来实现对方法的代理。
/*** JDK动态代理** @author lichlaughing*/@Componentpublic class JDKServiceDynamicProxy implements InvocationHandler {private Object target;public void setTarget(Object target) {this.target = target;}/*** @param proxy 代理对象* @param method 方法* @param args 参数* @return Object*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws InvocationTargetException, IllegalAccessException {System.out.println("UserServiceDynamicProxy before do");Object result = method.invoke(target, args);System.out.println("UserServiceDynamicProxy after do");return result;}public Object getProxyInstance() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}}
动态代理有唯一的工具类。Proxy来生成代理对象包括三个参数:
- 第一个参数,被代理对象的类加载器;
 - 第二个参数,被代理对象实现的所有接口数组;
 - 第三个参数InvocationHandler的实现类;
 
测试
@DisplayName("JDK动态代理")@Testpublic void jdkDynamicProxy() {AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext("cn.lichenghao.aop");// 目标对象UserService userService = (UserService) context.getBean("userService");// 代理对象JDKServiceDynamicProxy dynamicProxy= context.getBean(JDKServiceDynamicProxy.class);dynamicProxy.setTarget(userService);UserService userServiceProxy = (UserService) dynamicProxy.getProxyInstance();// 执行测试方法userServiceProxy.save(1);}
优点
JDK动态代理是JDK原生的,不需要任何依赖即可使用;
通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快;
缺点
如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;
JDK动态代理无法为没有在接口中定义的方法实现代理;
JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低;
CGLIB 代理
CGLIB(Code Generation Library),是一个强大的,高性能,高质量的字节码生成库。相比于JDK动态代理,完全不受代理类必须实现接口的限制,而且CGLIB底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。
同样需要准备代理方法类。
public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("cglib proxy before do");Object result = methodProxy.invokeSuper(o, objects);System.out.println("cglib proxy after do");return result;}}
测试代理
@DisplayName("CGLIB动态代理")@Testpublic void cglibDynamicProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserServiceImpl.class);enhancer.setCallback(new MyMethodInterceptor());UserService userServiceProxy = (UserService) enhancer.create();userServiceProxy.save(1);}
或者简化成工具
public class CglibDynamicProxy implements MethodInterceptor {private Class<?> parentClass;public void setParentClass(Class<?> parentClass) {this.parentClass = parentClass;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("cglib proxy before do");Object result = methodProxy.invokeSuper(o, objects);System.out.println("cglib proxy after do");return result;}public Object getProxyInstance() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(parentClass);enhancer.setCallback(this);return enhancer.create();}}
测试下
@DisplayName("CGLIB动态代理")@Testpublic void cglibDynamicProxy2() {CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy();cglibDynamicProxy.setParentClass(UserServiceImpl.class);UserService proxyInstance = (UserService) cglibDynamicProxy.getProxyInstance();proxyInstance.save(1);}
