简介: Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的一个重要特性,用于解耦和切割业务逻辑,是实现面向切面编程的重要手段之一。本文将介绍 Spring AOP 的原理及Spring AOP 的两种实现方式

Spring AOP实现原理

Spring AOP 是 Spring 框架提供的一种 AOP 实现方式,其核心原理是基于代理模式。它通过将目标对象(也就是被代理对象)包装成一个代理对象,从而实现对目标对象方法的动态拦截和处理,实现面向切面编程的目的。下面举个栗子。

假设你去医院看病,医生开了一些检查单让你去做,然后又给你开了一些药物和处方让你去取药。这里的医生就是一个代理对象,而你就是目标对象。医生不仅可以给你开药单,还可以对你的病情进行相应的诊断和建议,甚至可以对你的药物使用情况进行跟踪和评估。

在这个例子中,代理对象(医生)可以在你去做检查之前或取药之后,添加一些额外的逻辑处理。例如,医生可以检查你是否真的需要这些检查,或者是否有更好的药物替代方案。
这就类似于 Spring AOP 的代理对象在目标对象执行方法之前或之后,增加拦截和增强逻辑。

与此同时,代理对象还可以为目标对象屏蔽一些底层细节和对系统的影响,例如,医生可以提供一些健康咨询和建议,帮助你更好地理解自己的身体状况,而无需了解医学专业知识(也就是目标对象的内部实现)。这就类似于 Spring AOP 的代理对象可以为目标对象提供更好的封装和抽象,提高了系统的稳定性和安全性。

通过这个例子,我们可以更好地理解 Spring AOP 的代理原理,并深入理解代理对象和目标对象的区别和联系。代理对象相当于是目标对象的一个代理人,实现了对目标对象方法的拦截和处理,并承担了更多的职责和责任。同时,代理对象也可以起到更好的封装和抽象作用,提高了系统的可维护性和可扩展性。

Spring AOP 两种实现方式

默认使用 JDK 动态代理(如果目标对象实现了至少一个接口),如果目标对象没有实现接口,则使用 CGLIB 来代理对象。
可以通过配置参数 proxyTargetClass 来强制使用 CGLIB 代理,或者设置为 false 来强制使用 JDK 动态代理。如果不设置此参数,则根据上述规则来选择代理方式。

1.CGLIB

首先,定义好类,并且在类中添加需要增强的方法和切面逻辑。然后,定义一个切面类,调用 Enhancer.create 方法动态生成代理对象,并将 MethodInterceptor 对象传递给代理对象。最终,在 Spring 容器中进行配置和使用即可。
上代码

定义类和方法
定义一个类 UserService,其中有一个需要增强的 addUser 方法:

public class UserService {public void addUser(User user) {System.out.println("Add user: " + user.getName() + ", " + user.getAge());}
}

定义切面类

定义一个切面类 LogAspect,实现 MethodInterceptor 接口,并在 intercept 方法中编写增强逻辑:

@Component
public class LogAspect implements MethodInterceptor {@Overridepublic 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;}
}

配置 Spring AOP

在 Spring 配置文件中,定义 bean,并初始化切面类,通过 Enhancer.create 方法动态生成代理对象,并注入到容器中:

@Configuration
public class AppConfig {@Autowiredprivate LogAspect logAspect;@Beanpublic UserService userService() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class);enhancer.setCallback(logAspect);return (UserService) enhancer.create();}@Beanpublic LogAspect logAspect() {return new LogAspect();}
}

最后,在测试类中通过 Spring 容器获取 UserService 的代理对象,并调用其方法:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class UserServiceTest {@Autowiredprivate UserService userService;@Testpublic void testAddUser() {User user = new User();user.setName("Tom");user.setAge(20);userService.addUser(user);}
}

注解说明:

 - @Component:表示 LogAspect 是一个 Spring bean;- @Bean:表示该方法返回的对象将作为一个 bean 注入到 Spring 容器中;- Enhancer:CGLIB 提供的类,用于动态生成代理类;- setSuperclass:设置需要代理的父类;- setCallback:设置 MethodInterceptor 接口的实现类;- MethodInterceptor:CGLIB 提供的接口,用于拦截方法调用。

2.JDK 动态代理

首先,定义好接口,并且在实现类中添加需要增强的方法和切面逻辑。然后,定义一个切面类,实现 InvocationHandler 接口,并且在 invoke 方法中编写增强逻辑。最后,通过 Proxy 类动态生成代理对象,并将 InvocationHandler 对象传递给代理对象。最终,在 Spring 容器中进行配置和使用即可。

上代码

定义接口和实现类

定义一个接口 UserService 和一个实现类 UserServiceImpl,其中实现类中有一个需要增强的 addUser 方法:

public interface UserService {void addUser(User user);
}@Service
public class UserServiceImpl implements UserService {@Overridepublic void addUser(User user) {System.out.println("Add user: " + user.getName() + ", " + user.getAge());}
}

定义切面类

定义一个切面类 LogAspect,并在其 before 方法中实现增强逻辑:

@Component
@Aspect
public class LogAspect {@Before("execution(* com.example.demo.service.UserService.addUser(..)) && args(user)")public void before(User user) {System.out.println("Before adding user: " + user.getName() + ", " + user.getAge());}
}

配置 Spring AOP

在 Spring 配置文件中,配置开启 Spring AOP 的注解支持,并扫描切面类:

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.example.demo")
public class AppConfig {}

最后,在测试类中通过 Spring 容器获取 UserService 的代理对象,并调用其方法:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class UserServiceTest {@Autowiredprivate UserService userService;@Testpublic void testAddUser() {User user = new User();user.setName("Tom");user.setAge(20);userService.addUser(user);}
}

注解说明:

  • @Aspect:表示 LogAspect 是一个切面类;
  • @Before:表示 before 类型的通知,在目标方法执行前执行增强逻辑;
  • @EnableAspectJAutoProxy:表示开启 Spring AOP 的自动代理功能,会自动使用 JDK 动态代理;同时,由于没有 proxyTargetClass 参数(默认为 false ),所以此处使用 JDK 动态代理代理类。

3.CGLIB与JDK动态代理的优缺点

JDK动态代理

JDK动态代理是指在运行时动态生成代理类,实现面向切面编程,并且只能代理实现了接口的类。在 Spring AOP 中,JDK 动态代理基于 InvocationHandler 进行 AOP 操作。
优点

  • 轻量级:JDK动态代理只需要通过反射调用方法,比CGLib更加轻量级;
  • 可扩展性:由于只针对接口生成代理类,因此支持类的动态加载,以及对新接口方法的支持,更具有扩展性;
  • 安全性:原始类和代理类之间只有接口的关系,不会动态生成类的子类,不存在类的继承关系,更加安全。

缺点

  • 只能基于接口生成代理对象,无法对类进行代理;
  • 对目标类的要求较高,目标类必须实现接口。

CGLib

CGLib 全称是Code Generation Library,是二者中性能较高的一种代理方式,它通过动态生成目标类的子类,并在子类中拦截代理类的方法调用。
优点

  • 性能高: CGLib 采用了底层字节码技术进行代理,性能比 JDK 动态代理更高;
  • 无需实现接口: 目标类不需要实现接口,也能被代理,更加灵活。

缺点

  • 不能代理 final 方法和 final 类;
  • 由于采用继承方式实现,可能会覆盖掉默认的构造函数,需要手动实现。

综上所述,JDK动态代理和CGLib各有其优点和缺点。如果目标类已经实现了接口,建议采用 JDK 动态代理;如果目标类没有实现接口,采用 CGLib 会更合适。 当然,如果没有特殊需求,采用默认的 JDK 动态代理也可以实现较好的 AOP 功能。

【Spring AOP(2)篇】原理及两种实现方式(cglibjdk动态代理)相关推荐

  1. Java框架篇---spring aop两种配置方式

    Java框架篇---spring aop两种配置方式 第一种:注解配置AOP 注解配置AOP(使用 AspectJ 类库实现的),大致分为三步:  1. 使用注解@Aspect来定义一个切面,在切面中 ...

  2. Spring AOP两种使用方式以及如何使用解析

    AOP是一种面向切面编程思想,也是面向对象设计(OOP)的一种延伸. 在Spring实现AOP有两种实现方式,一种是采用JDK动态代理实现,另外一种就是采用CGLIB代理实现,Spring是如何实现的 ...

  3. Spring AOP拦截规则的两种定义方式

    Spring AOP拦截规则的两种定义方式 AOP的根本目的就是解耦,分开业务代码与系统共用代码,例如打印日志. Spring支持AspectJ的注解式切面编程,主要包含4个部分,分别是 使用@Asp ...

  4. Spring AOP的实现原理及应用场景(通过动态代理)

    点击关注公众号,利用碎片时间学习 AOP的作用 作用:在不修改源代码的情况下,可以实现功能的增强. 传统的纵向体系代码复用: 横向抽取机制(AOP思想): AOP 思想:基于代理思想,对原来目标对象, ...

  5. 4种实例 advice aop_Java动态代理在Spring的应用:AOP编程与动态代理知识

    认真写文章,用心做分享.公众号:Java耕耘者 文章都会在里面更新,整理的资料也会放在里面. 关于代理模式的话题有很多,在开发中经常用到的应该是静态代理模式,能很好的去耦合. 动态代理是代理模式的另外 ...

  6. Spring AOP底层实现原理(动态代理)

    什么是AOP? AOP(面向切面编程)通过预编译的方式 和 运行期动态代理的方式来实现程序功能统一维护的一种方式,是OOP(面向对象编程)的延续.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业 ...

  7. AOP 详解 、AOP 中通知类型 、AOP 两种实现方式(Schema-base 和 AspectJ)

    一.AOP AOP:中文名称面向切面编程 英文名称:(Aspect Oriented Programming) 正常程序执行流程都是纵向执行流程 3.1 又叫面向切面编程,在原有纵向执行流程中添加横切 ...

  8. Spring加载properties文件的两种方式

    2019独角兽企业重金招聘Python工程师标准>>> 在项目中如果有些参数经常需要修改,或者后期可能需要修改,那我们最好把这些参数放到properties文件中,源代码中读取pro ...

  9. Spring与Hibernate两种组合方式

    Spring与Hibernate大致有两种组合方式,主要区别是一种是在Hibernate中的hibernate.cfg.xml中配置数据源,一种是借助Spring的jdbc方式在Spring的appl ...

最新文章

  1. uC/OS II--与ECB操作相关的四个函数
  2. 在Delphi7中调试COM
  3. CommandLineRunner与ApplicationRunner接口的使用及源码解析
  4. 错误: 找不到或无法加载主类 org.apache.hadoop.hbase.util.GetJavaProperty
  5. 为什么现在还有很多人喜欢在银行存定期?
  6. 《数据库系统概念》14-静态散列
  7. 二级Access数据库大纲知识要点
  8. Python的下载安装图文教程(超详细!!!)
  9. HTML字体及文本样式
  10. C盘Temp文件夹的内容可以删掉
  11. python数据驱动创建账号_20190705-Python数据驱动之DDT
  12. qlv文件是什么?qlv文件格式介绍
  13. 安装与破解IntelliJ IDEA2017
  14. Mongodb的Min key和Max key是能查找最大最小值的新功能吗?
  15. 文件服务器恢复测试,基于文件传输中文件损坏检测和恢复办法.doc
  16. arduino仿真平台有哪些
  17. 解决提交到github报错Please tell me who you are
  18. 一颗小行星掠过地球附近
  19. 自动修改域用户计算机名,Windows自动改计算机名和加入域工具
  20. python | 统计频次

热门文章

  1. 一个互联网『打工人』的卑微一天
  2. 【C/C++】获取计算机CPUID序列号
  3. 《ROS机器人程序设计》期末测评试卷 (ROS2)
  4. 2018年度总结和2019年度计划
  5. 虚拟现实技术-给人脸加眼镜
  6. 命令行查看、设置和取消git或终端代理
  7. python之基础摘抄题
  8. sql语句的书写顺序以及执行顺序
  9. Kooboo完全介绍二:创建第一个Kooboo站点
  10. springboot搭建http2服务器和h2c服务器 h2 的http/https 请求服务器