定义

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 方面配置的支持,需要在配置文件中添加如下配置:

  1. <aop:aspectj-autoproxy/>

实现原理:
如果我们配置了AOP,那么 Spring 在创建相关的 bean 的时候,会给这个 bean 一个代理对象,我们后续对 bean 中方法的调用,实际上调用的是代理类重写的代理方法。
Spring的 AOP 使用了两种动态代理,分别是 JDK 的动态代理以及 CGLib 的动态代理:

  1. 被代理的类有接口 , 使用 JDK 的动态代理,创建接口实现类代理对象,增强类的方法
  2. 被代理的类没有接口,使用 CGLIB 代理,创建子类的代理对象,增强类的方法

:::tips 在J AVA平台中,对于 AOP 增强,有三种方式:

  1. 编译期:编译器把切面的调用编译进字节码,这种方式需要扩展编译器;
  2. 类加载器:目标被加载到 JVM,通过特殊的加载器,对目标字节码进行增强;
  3. 运行期:目标对象和切面都是 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 代理

静态代理

准备测试目标接口

  1. public interface UserService {
  2. void save(int a);
  3. void get();
  4. void delete(int id);
  5. }
  6. @Service("userService")
  7. public class UserServiceImpl implements UserService {
  8. public void save(int a) {
  9. System.out.println("save 方法执行");
  10. }
  11. public void get() {
  12. System.out.println("get 方法执行");
  13. }
  14. public void delete(int id) {
  15. System.out.println("delete 方法执行");
  16. }
  17. }

静态代理,纯手动

  1. @Component
  2. public class UserServiceProxy implements UserService {
  3. private final UserServiceImpl userService = new UserServiceImpl();
  4. @Override
  5. public void save(int a) {
  6. System.out.println("proxy save method invoke");
  7. userService.save(a);
  8. }
  9. @Override
  10. public void get() {
  11. }
  12. @Override
  13. public void delete(int id) {
  14. }
  15. }

测试

  1. @DisplayName("JDK静态代理")
  2. @Test
  3. public void jdkStaticProxy() {
  4. AnnotationConfigApplicationContext context
  5. = new AnnotationConfigApplicationContext("cn.lichenghao.aop");
  6. UserServiceProxy userServiceProxy = context.getBean(UserServiceProxy.class);
  7. userServiceProxy.save(1);
  8. }

优点简单;缺点也很明显需要给每个类手动设置代理,如果目标对象很多的话…..

动态代理

需要实现InvocationHandler来实现对方法的代理。

  1. /**
  2. * JDK动态代理
  3. *
  4. * @author lichlaughing
  5. */
  6. @Component
  7. public class JDKServiceDynamicProxy implements InvocationHandler {
  8. private Object target;
  9. public void setTarget(Object target) {
  10. this.target = target;
  11. }
  12. /**
  13. * @param proxy 代理对象
  14. * @param method 方法
  15. * @param args 参数
  16. * @return Object
  17. */
  18. @Override
  19. public Object invoke(Object proxy, Method method, Object[] args)
  20. throws InvocationTargetException, IllegalAccessException {
  21. System.out.println("UserServiceDynamicProxy before do");
  22. Object result = method.invoke(target, args);
  23. System.out.println("UserServiceDynamicProxy after do");
  24. return result;
  25. }
  26. public Object getProxyInstance() {
  27. return Proxy.newProxyInstance(target.getClass().getClassLoader()
  28. , target.getClass().getInterfaces(), this);
  29. }
  30. }

动态代理有唯一的工具类。Proxy来生成代理对象包括三个参数:

  • 第一个参数,被代理对象的类加载器;
  • 第二个参数,被代理对象实现的所有接口数组;
  • 第三个参数InvocationHandler的实现类;

测试

  1. @DisplayName("JDK动态代理")
  2. @Test
  3. public void jdkDynamicProxy() {
  4. AnnotationConfigApplicationContext context
  5. = new AnnotationConfigApplicationContext("cn.lichenghao.aop");
  6. // 目标对象
  7. UserService userService = (UserService) context.getBean("userService");
  8. // 代理对象
  9. JDKServiceDynamicProxy dynamicProxy
  10. = context.getBean(JDKServiceDynamicProxy.class);
  11. dynamicProxy.setTarget(userService);
  12. UserService userServiceProxy = (UserService) dynamicProxy.getProxyInstance();
  13. // 执行测试方法
  14. userServiceProxy.save(1);
  15. }

优点
JDK动态代理是JDK原生的,不需要任何依赖即可使用;
通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快;
缺点
如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;
JDK动态代理无法为没有在接口中定义的方法实现代理;
JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低;

CGLIB 代理

CGLIB(Code Generation Library),是一个强大的,高性能,高质量的字节码生成库。相比于JDK动态代理,完全不受代理类必须实现接口的限制,而且CGLIB底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。

同样需要准备代理方法类。

  1. public class MyMethodInterceptor implements MethodInterceptor {
  2. @Override
  3. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  4. System.out.println("cglib proxy before do");
  5. Object result = methodProxy.invokeSuper(o, objects);
  6. System.out.println("cglib proxy after do");
  7. return result;
  8. }
  9. }

测试代理

  1. @DisplayName("CGLIB动态代理")
  2. @Test
  3. public void cglibDynamicProxy() {
  4. Enhancer enhancer = new Enhancer();
  5. enhancer.setSuperclass(UserServiceImpl.class);
  6. enhancer.setCallback(new MyMethodInterceptor());
  7. UserService userServiceProxy = (UserService) enhancer.create();
  8. userServiceProxy.save(1);
  9. }

或者简化成工具

  1. public class CglibDynamicProxy implements MethodInterceptor {
  2. private Class<?> parentClass;
  3. public void setParentClass(Class<?> parentClass) {
  4. this.parentClass = parentClass;
  5. }
  6. @Override
  7. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  8. System.out.println("cglib proxy before do");
  9. Object result = methodProxy.invokeSuper(o, objects);
  10. System.out.println("cglib proxy after do");
  11. return result;
  12. }
  13. public Object getProxyInstance() {
  14. Enhancer enhancer = new Enhancer();
  15. enhancer.setSuperclass(parentClass);
  16. enhancer.setCallback(this);
  17. return enhancer.create();
  18. }
  19. }

测试下

  1. @DisplayName("CGLIB动态代理")
  2. @Test
  3. public void cglibDynamicProxy2() {
  4. CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy();
  5. cglibDynamicProxy.setParentClass(UserServiceImpl.class);
  6. UserService proxyInstance = (UserService) cglibDynamicProxy.getProxyInstance();
  7. proxyInstance.save(1);
  8. }