作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏
当前专栏:Spring5应用专栏_Aomsir的博客
前言
在上一篇文章中,我讲述了Spring动态代理底层具体实现之一的JDK动态代理,并在结尾抛出了本篇文章将要讲述的内容,本篇文章我将会着手于Spring动态代理底层具体实现的另一门技术之Cglib
Cglib动态代理
在上一章关于JDK动态代理的讨论中,我们确实注意到了其一个明显的限制:原始类和代理类都必须实现同一个接口
,代理类方法添加额外功能并调用原始类方法。这种方式就像房屋中介:他们为租客提供服务,而租客可能并不知道背后的真正房东。但这种模式确实存在局限性,特别是当我们的系统设计中没有采用接口,或者原始类并没有实现任何接口而是直接写具体方法实现时。
那么,面对这种情况,我们怎么办?我们如何实现动态代理而不受到"必须实现接口"这一限制的约束?为了解决这个问题,Cglib(Code Generation Library)
应运而生。Cglib为我们提供了一种不基于接口的代理实现方式,它可以直接代理类。相对于JDK的代理方式,Cglib是基于继承父类(原始类)的,它会动态生成一个子类来覆盖原始类的方法,从而实现代理功能。这样既可以保证二者方法一致,这样既可以保证在代理类中提供新的实现,同时又调用了原始方法
这种方法提供了更大的灵活性,特别是对于那些没有接口的类。在接下来的章节中,我们将深入探讨Cglib,了解它是如何提供这种强大功能的,以及它与JDK动态代理的区别和优缺点
开发步骤
使用Cglib创建动态代理的过程在逻辑上与JDK动态代理非常相似,但主要差异在于原始类不需要实现任何接口。以下是具体的步骤:
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
public void login(String name, String password) {
log.info("name = {}, password = {}", name, password);
}
public void register(User user) {
log.info("{}", user);
}
}
public class TestCglibProxy {
private static final Logger log = LoggerFactory.getLogger(TestCglibProxy.class);
@Test
public void test1() {
// 创建原始对象
UserService userService = new UserService();
// 创建Cglib提供的Enhancer对象,用于设置好创建代理对象
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(TestCglibProxy.class.getClassLoader());
// 设置被代理类(原始父类)
enhancer.setSuperclass(UserService.class);
// 设置方法拦截器
MethodInterceptor methodInterceptor = new MethodInterceptor() {
// 等同于InvocationHandler中的invoke方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
log.debug("log before");
Object ret = method.invoke(userService, objects);
log.debug("log after");
return ret;
}
};
enhancer.setCallback(methodInterceptor);
// 创建代理对象
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("Aomsir", "123456");
}
}
总结
经过前两篇文章的深入探讨,我们对Spring动态代理的底层实现有了更为全面的了解。其中,JDK动态代理和Cglib动态代理是两种最为核心的技术手段。
JDK动态代理以接口为核心,通过Proxy.newInstance()
方法为我们提供了创建代理对象的能力,要求原始类必须实现某个接口。而Cglib动态代理则更为灵活,不受接口的限制。通过Enhancer.create()
,它直接生成原始类的子类,并在其中嵌入额外功能,从而实现代理。当然我们也可以通过设置让Spring在创建的时候只使用Cglib而不使用JDK
Spring AOP聪明地整合了这两种机制,为开发者提供了无缝的体验。它默认采用JDK动态代理,但当遇到没有实现接口的原始类时,会智能切换到Cglib动态代理。
现在,再回过头来重新审视 BeanPostProcessor
,我们可以发现更多之前可能忽略的细节和深层次的关联。正是这些细节和关联,使得Spring AOP的实现如此高效且灵活,满足了开发者的各种需求
参考文献
- 孙哥孙帅suns说Spring5~学不会Spring? 因为你没找对人
- Spring官方文档