Appearance
1、什么是AOP
AOP(Aspect-Oriented Programming:面向切面编程)是一种编程范式,通过在代码中横向插入关注点(如日志、事务),实现对应用程序模块化、可维护性和可重用性的提升。
AOP 切面编程涉及到的一些专业术语:
| 术语 | 含义 |
|---|---|
| 目标(Target) | 被通知的对象 |
| 代理(Proxy) | 向目标对象应用通知之后创建的代理对象 |
| 连接点(JoinPoint) | 目标对象的所属类中,定义的所有方法均为连接点 |
| 切入点(Pointcut) | 被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点) |
| 通知(Advice) | 增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情 |
| 切面(Aspect) | 切入点(Pointcut)+通知(Advice) |
| Weaving(织入) | 将通知应用到目标对象,进而生成代理对象的过程动作 |
2、Spring AOP 的实现
Spring AOP基于动态代理实现,
- 对于实现了接口的对象,使用JDK Proxy创建代理对象;
- 对于未实现接口的对象,使用Cglib生成被代理对象的子类。
Spring AOP还集成了AspectJ,AspectJ是Java生态系统中最完整的AOP框架,拥有更多功能,特别在切面较多的情况下,选择AspectJ相比Spring AOP更为高效。
Spring的AOP(面向切面编程)允许开发者定义横切关注点(如日志、事务管理等),这些关注点通常与业务逻辑的主流程解耦,可以跨越应用的多个点。AOP通过在运行时动态地将代码注入到指定位置来实现这一点,而这正是通过“代理”完成的。
实现AOP的方式主要有两种:
- JDK动态代理:适用于那些实现了接口的对象。它通过反射机制在运行时创建代理对象,将横切逻辑织入到这些对象中。
- CGLIB代理:适用于没有实现接口的对象。它通过底层的字节码技术生成被代理对象的子类,并在子类中注入横切逻辑。
动态代理
介绍一下相关的功能
在 Java AOP (面向切面编程)中,动态代理是一种实现机制,它在运行时创建一个实现了一组接口的代理对象。这个代理对象可以在原有对象方法调用前后执行额外的操作(如日志记录、事务管理等),而无需修改原有对象的代码。这种机制主要通过两种方式实现:基于接口的 JDK 动态代理和基于类的代理(例如使用 CGLIB 或 ByteBuddy)。
JDK 动态代理
JDK 动态代理使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现。这种代理方式要求目标对象必须实现一个或多个接口。
工作原理
- 接口实现:动态代理类必须实现目标类的一个或多个接口。
- 创建代理实例:通过
Proxy.newProxyInstance方法创建代理实例。这个方法需要:- 类加载器(用于定义代理类)
- 一组接口(代理类将实现的接口)
InvocationHandler实现(所有方法调用都会转发到这个调用处理器)
- 方法调用:当代理实例的方法被调用时,调用会被转发到
InvocationHandler的invoke方法。在invoke方法中,可以在调用目标方法之前或之后执行其他操作。
通过 point.getTarget() 获取目标实例
在 AOP 框架中,当方法执行被拦截(如通过切点指定的方法),AOP 框架创建一个 JoinPoint 实例,该实例封装了有关当前方法调用的信息。JoinPoint 提供了 getTarget() 方法,该方法返回被代理的目标对象(原始对象),即使当前执行的是代理方法。因此,即使我们操作的是代理实例,我们也可以通过 getTarget() 访问到原始的目标类实例。
CGLIB 动态代理
CGLIB(Code Generation Library)是另一种动态代理方式,它不需要接口。CGLIB 通过继承目标类并在运行时生成子类来实现代理。
工作原理
- 子类生成:CGLIB 在运行时生成目标类的子类,并覆盖其方法以提供增强的功能。
- 方法调用:当调用代理实例的方法时,实际上是调用由 CGLIB 生成的子类中的方法。在这些方法中,可以在调用实际的目标方法前后插入自定义的行为。
使用场景与选择
- JDK 动态代理适用于目标对象实现了接口的情况。由于只需代理接口方法,这种方式不会代理类的属性,通常性能较好。
- CGLIB 动态代理适用于目标对象没有实现任何接口的情况。它通过生成子类来实现代理,更为强大但通常比 JDK 动态代理略慢。
总的来说,AOP 通过动态代理技术提供了一个非常强大的机制,允许开发者在不修改原始代码的情况下,增加额外的功能。通过 point.getTarget() 获取到的是代理调用中的原始对象,确保了即使在代理环境中,也能正确地处理业务逻辑。
参考