在 Java 开发的奇妙世界里,Spring 框架就像一位超级英雄,在各种企业级项目中 “大显身手”,帮我们解决了数不清的难题。而 Spring 动态代理作为 Spring 框架中的一项 “秘密武器”,更是为开发者们带来了无限可能。今天,就让我们一起揭开 Spring 动态代理的神秘面纱,看看它到底有什么神奇之处!
-
什么是动态代理
在深入了解 Spring 动态代理之前,我们得先搞清楚什么是代理。想象一下,你想买一张热门演唱会的门票,但又怕自己抢不到,这时候你就可以找一个 “靠谱” 的票务代理帮你抢票。这个票务代理就相当于我们程序中的代理对象,它帮你去做一些你不太方便或者不想直接做的事情。在程序里,代理模式就是为其他对象提供一个 “代理替身”,通过这个替身来控制对目标对象的访问。简单来说,代理对象就像是一座桥梁,把客户端和目标对象连接起来,客户端不用直接和目标对象打交道,而是通过代理对象来间接访问。
代理分为静态代理和动态代理。静态代理就像是提前定制好的衣服,在编译期就已经把代理类的代码写死了,一旦做好,就很难修改。而动态代理则像是一件可以根据你的身材随时调整的 “智能衣服”,它是在程序运行的时候,根据实际需要动态生成代理类及其实例的。动态代理最大的好处就是,我们不用去修改目标对象的代码,就能轻松地给它添加各种 “额外功能”,就像给手机安装各种 APP 一样,想装什么功能就装什么功能,这大大提高了代码的可维护性和扩展性,是不是很厉害?
-
Spring 动态代理的原理
Spring 动态代理主要是借助 Java 的反射机制来实现的,它就像一个 “魔法工厂”,能根据我们的需求生产出各种神奇的代理对象。Spring 提供了两种 “魔法配方” 来制作代理对象,分别是 JDK 动态代理和 CGLIB 动态代理。
(一)JDK 动态代理
JDK 动态代理是 Java 自带的 “魔法”,它就像一个挑剔的 “小工匠”,要求目标对象必须实现至少一个接口。为什么呢?因为 JDK 动态代理是通过接口来创建代理对象的,就像你要制作一把钥匙,必须先有一个锁的模具(接口)才行。
JDK 动态代理有两个核心类,一个是Proxy,另一个是InvocationHandler。InvocationHandler就像是一个 “幕后指挥官”,它是一个接口,我们需要实现它的invoke方法,在这个方法里编写我们的代理逻辑。比如说,你想在调用目标对象的方法之前先检查一下权限,或者在方法调用之后记录一下日志,这些都可以在invoke方法里实现。
而Proxy类则像是一个 “神奇的工厂”,它的newProxyInstance方法就是生产代理对象的 “魔法机器”。我们只需要给它传入类加载器(就像是工厂的能源供应)、目标对象实现的接口数组(就像是制作钥匙的模具)以及InvocationHandler实例(就像是工厂的指挥官),它就能帮我们生成一个代理对象。当我们调用这个代理对象的方法时,其实就像是按下了工厂的启动按钮,真正执行的是InvocationHandler的invoke方法。在这个方法里,我们可以先做一些 “准备工作”(前置处理),然后通过反射调用目标对象的实际方法,最后再做一些 “收尾工作”(后置处理)。
(二)CGLIB 动态代理
CGLIB(Code Generation Library)是一个超级强大且高性能的代码生成库,它就像一个 “万能的工匠”,即使目标对象没有实现接口,它也能施展 “魔法” 创建代理对象。
CGLIB 是怎么做到的呢?它采用了继承的方式,就像让一个孩子继承父母的优点一样,CGLIB 会在运行时生成目标类的一个子类,这个子类就像是目标类的 “超级模仿者”,它重写了目标类的所有非 final 方法,并且在这些方法中巧妙地织入了我们的代理逻辑。
CGLIB 动态代理的核心类是Enhancer,它就像是 CGLIB 的 “魔法棒”。我们只需要设置Enhancer的父类为目标类(告诉它要模仿谁),再设置回调函数(类似于 JDK 动态代理中的InvocationHandler,它是代理逻辑的执行者),然后轻轻挥动 “魔法棒”(调用create方法),就能创建出一个代理对象啦!
-
Spring 动态代理的例子
光说不练假把式,下面我们就通过具体的代码示例来看看 Spring 动态代理是怎么 “大显神通” 的。
publicinterfaceUserService {
voidaddUser();
}
然后,我们实现这个接口的目标类UserServiceImpl,它就像是一个 “勤劳的小蜜蜂”,负责具体执行任务。
publicclassUserServiceImplimplementsUserService {
publicvoidaddUser() {
System.out.println("添加用户");
}
}
接着,我们要实现InvocationHandler接口,它就像是一个 “交通指挥员”,负责指挥代理对象的行动。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
publicclassUserServiceInvocationHandlerimplementsInvocationHandler {
privateObject target;
publicUserServiceInvocationHandler(Object target) {
this.target = target;
}
publicObjectinvoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用前的处理,比如检查权限");
Object result = method.invoke(target, args);
System.out.println("方法调用后的处理,比如记录日志");
return result;
}
}
最后,我们来测试 JDK 动态代理,就像是在舞台上表演一场魔术。
import java.lang.reflect.Proxy;
publicclassJDKProxyTest {
publicstaticvoidmain(String[] args) {
UserService target = newUserServiceImpl();
UserServiceInvocationHandler handler = newUserServiceInvocationHandler(target);
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
proxy.addUser();
}
}
二)CGLIB 动态代理示例
首先,我们定义一个没有实现接口的目标类OrderService,它就像是一个 “独行侠”,自己做自己的事情。
publicclassOrderService {
publicvoidaddOrder() {
System.out.println("添加订单");
}
}
然后,我们实现 CGLIB 的回调接口MethodInterceptor,它就像是 CGLIB 的 “智囊团”,为代理对象出谋划策。
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
publicclassOrderServiceMethodInterceptorimplementsMethodInterceptor {
publicObjectintercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法调用前的处理,比如检查库存");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("方法调用后的处理,比如更新订单状态");
return result;
}
}
最后,我们来测试 CGLIB 动态代理,见证 “魔法” 的时刻。
import net.sf.cglib.proxy.Enhancer;
publicclassCGLIBProxyTest {
publicstaticvoidmain(String[] args) {
Enhancer enhancer = newEnhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(newOrderServiceMethodInterceptor());
OrderService proxy = (OrderService) enhancer.create();
proxy.addOrder();
}
}
原文始发于微信公众号(代码小铺):一文读懂 Spring 动态代理
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论