代理创建的 3 要素:
- 原始对象;
- 额外功能;
- 代理对象和原始对象实现相同的接口。
一、JDK 动态代理
JDK 实现的动态代理主要是通过 java.lang.reflect
包下的 Proxy
类实现的。
通过调用该类的 newProxyInstance
方法:
newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
该方法的参数详解如下:
- ClassLoader:类加载器;
- interfaces:原始对象实现的接口;
InvocationHandler
:额外功能。
后两个参数比较好理解,因为文章的开头说了,动态代理的 3 要素,原始对象、额外功能、实现相同的接口。
实现相同的接口和额外功能我们都提供了,那么为什么还要提供一个类加载器呢?这里做如下解释:
首先明确类加载器的作用:
- 通过类加载器把对应类的字节码加载进 JVM;
- 通过类加载器创建类的 Class 对象,进而创建这个类的对象。
每一个类的 .class 文件自动分配与之对应的 ClassLoader。
而我们这里没有创建出代理类,所以就没有源文件,就没有 .class 文件,也就不会分配 ClassLoader,这里采用的是动态字节码技术:
动态字节码技术:创建字节码。
在动态代理创建的过程中,需要 classloader 创建代理类的 Class 对象;
可是因为动态代理没有对应的 .class 文件,JVM也就不会为其分配 ClassLoader,但是又需要,所以借用一个 classloader;
具体借用谁的无所谓。
InvocationHandler 是一个接口,我们可以写个匿名内部类,也可以直接使用 Lambda 表达式实现,这里用 Lambda 演示:
(proxy, method, arguments) -> {
System.out.println("TestJDKProxy.main");
return method.invoke(userService, arguments);
}
- proxy:原始对象;
- method:待增强的方法;
- arguments:方法的参数。
我们可以通过返回值将代理过的对象返回。
public class TestJDKProxy {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
/*
* 1. 借一个 classloader,谁的都行;
* 2. 原始对象实现的接口;
* 3. 额外功能。
* */
UserService proxyInstance = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(),
(proxy, method, arguments) -> {
System.out.println("TestJDKProxy.main");
return method.invoke(userService, arguments);
});
proxyInstance.login();
proxyInstance.register();
}
}
二、CGlib 动态代理
JDK 的方式是原始对象必须实现一个接口,才能进行动态代理,如果不实现接口,可以动态代理吗?
也是可以的,这就要使用 CGlib 的动态代理了,他的代理类继承自原始类,所以也会有原始类的方法。
正如 JDK 的动态代理一样,cglib 也有一个核心类,就是 Enhance 类:
- 他也需要一个 ClassLoader;
- 由于是通过继承实现的,所以需要设置父类;
- 最后设置一个与
InvocationHandler
类似的回调接口。
最后调用 enhancer.create()
方法执行。
public static void main(String[] args) {
UserService userService = new UserService();
// 通过 cglib 创建动态代理对象
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCglib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
/*
* method 是原始方法;
* objects 是原始方法参数;
*
* 这里使用的是 Lambda 表达式的形式。
*/
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("cglib 的动态代理");
return method.invoke(userService, objects);
});
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("ws", "1234");
userServiceProxy.register(new Person());
}
输出结果:
cglib 的动态代理
UserService.login
cglib 的动态代理
UserService.register
三、总结
- JDK 动态代理:
Proxy.newInstance()
通过接口创建代理的实现类; - Cglib 动态代理:
Enhancer
通过继承父类创建的代理类;