作者简介 :☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页 :Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏
当前专栏 :Spring5应用专栏_Aomsir的博客
前言
在上一篇文章: 《Spring5应用之静态代理开发》中,我们详细地探讨了静态代理开发所面临的诸多问题,如代码冗余、维护困难 等问题。为了克服这些问题,本文将引导大家深入了解Spring5的动态代理开发
何为动态代理?
通过代理类为原始类添加附加功能,这样做的优点是有利于原始类的维护与扩展。其作用和优势与静态代理相同。然而,动态代理与静态代理最明显的不同在于它们的开发流程和底层实现方式
开发编码
- 引入必要的动态代理相关jar包:确保项目中有所需的依赖库,以支持动态代理的实现。
- 创建并配置原始对象(目标对象):创建需要被代理的原始对象,并将其注入到Spring容器中。
- 实现MethodBeforeAdvice接口:此接口用于在目标方法调用之前添加额外的功能。完成此实现后,也应将其注入到Spring容器中。
- 定义切入点:切入点指定在哪些特定的原始方法上需要添加额外的功能。
- 进行配置整合:将原始对象、增强功能及切入点配置到Spring中,确保它们可以正确地协同工作。
- 编写测试类:为了验证动态代理的效果,可以编写测试类并通过原始对象的ID从容器中获取代理对象,进而调用相关方法进行验证
xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
java
public class Before implements MethodBeforeAdvice {
private static final Logger log = LoggerFactory.getLogger(Before.class);
/**
* 把需要在原始方法执行之前的功能,书写在before方法中
* @param method 原始方法
* @param objects 原始方法的参数
* @param o 原始方法所在的对象
* @throws Throwable 异常
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
log.info("-------method before advice log-------");
}
}
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.aomsir.basic.proxy.service.impl.UserServiceImpl" />
<bean id="before" class="com.aomsir.basic.proxy.dynamic.Before" />
<aop:config>
<!--定义切入点,将额外方法加在所有类的方法上-->
<aop:pointcut id="pc" expression="execution(* * (..))"/>
<!--组装,将额外功能与切入点进行组装整合-->
<aop:advisor advice-ref="before" pointcut-ref="pc" />
</aop:config>
</beans>
java
public class TestProxy {
@Test
public void test2() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login("Aomsir", "123456");
userService.register(new User());
}
}
测试结果
从图1的测试结果中,我们可以清晰地观察到在UserService中的两个方法上都已经成功添加了额外的功能。这证明了我们的动态代理配置起到了预期的作用。
进一步地,从图2中我们可以看到一个Debug的方法调用过程。仔细观察后,可以发现通过原始类型id获取的对象已不再是初始的原始对象。相反,它现在是一个经过整合和配置后的代理对象 ,这进一步证实了动态代理的实际效果
动态代理细节分析
Spring创建的动态代理类在什么地方?
通过前述的编码流程和测试结果展示,我们可以清晰地了解到Spring已经为我们自动完成了目标类与额外功能的整合工作。但这时候一个问题出现了:那动态代理类存放在何处?在我们的程序中并没有明确地看到这个类。
其秘密在于,Spring框架利用了动态字节码技术 。当程序运行时,Spring会在JVM内部实时地生成这个动态代理类。由于这个类仅仅是在运行时动态创建的,所以当程序执行结束后,它将随着JVM的关闭而消失
什么叫动态字节码技术?
我们已经了解到,Spring通过动态字节码技术来创建动态代理类。但动态字节码技术到底是什么?
当Java程序运行时,是由JVM加载类的字节码文件(.class)来执行的。而动态字节码技术,正是利用一些第三方框架如ASM、javassist、Cglib等来实现的。这些框架能够帮助我们直接在JVM内部生成动态的字节码(.class)
,进而创建相应的代理对象,而无需在文件系统中实际生成一个物理文件
。
当JVM进程结束后,这些动态生成的字节码同样会被清除。这意味着,与静态代理不同,动态代理不会产生实际的类文件。因此,它避免了因为类文件数量众多而可能给项目管理带来的麻烦。
总结
在今天的《Spring5之AOP动态代理开发》
主题中,我并没有进行深入细致的AOP分析。不过,不用担心,接下来的系列文章中,我会逐步带领大家深入探讨Spring AOP的各个知识点,包括切入点表达式、MethodInterceptor、动态代理的底层实现原理等内容