微信搜索《Java鱼仔》,每天一个知识点不错过
(一)每天一个知识点
关于动态代理,你能说出动态代理的几种方式?
(二)详解
动态代理是指代理类不是写在代码中的,而是在代码运行过程中产生的,Java提供了两种方式来实现动态代理,分别是基于Jdk的动态代理和基于Cglib的动态代理。
2.1 基于Jdk的动态代理
首先我们来搭建一套实现动态代理的实现,以租房为例,我新建一个租房的接口:
public interface Room {
void rent();
}
然后下一个实际的租房实现类
public class RealRoom implements Room {
private String roomname;
public RealRoom(String roomname) {
this.roomname = roomname;
}
public void rent() {
System.out.println("租了"+roomname);
}
}
基于Jdk的动态代理核心在于InvocationHandler 接口,首先我们新建一个类ProxyHandler来实现这个InvocationHandler 接口,并实现里面的invoke方法
public class ProxyHandler implements InvocationHandler {
Object object;
public ProxyHandler(Object object) {
this.object = object;
}
//proxy 代理对象
//method 要实现的方法
//args 方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行之前:"+method.getName());
Object invoke = method.invoke(object, args);
System.out.println("代理执行之后:"+method.getName());
return invoke;
}
}
InvocationHandler 是一个接口,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类invoke,由它实现处理内容。
接下来写一个方法来实现动态代理
public static void main(String[] args) {
Room room=new RealRoom("碧桂园");
//obj.getClass().getClassLoader()类加载器
//obj.getClass().getInterfaces() 目标类实现的接口
//InvocationHandler对象
InvocationHandler invocationHandler=new ProxyHandler(room);
Room proxyRoom = (Room) Proxy.newProxyInstance(room.getClass().getClassLoader(), room.getClass().getInterfaces(), invocationHandler);
proxyRoom.rent();
}
这段代码的另外一个核心是Proxy.newProxyInstance,该方法需要三个参数,目的是运行期间生成代理类,每个参数的功能已经写在了注释中。这段代码的意思就是Proxy 动态产生的代理对象会调用 InvocationHandler 实现类invoke。
最终我们不需要RealRoom去调用rent方法,通过代理类就可以实现这个rent方法。
最后结果如下:
代理执行之前:rent
租了碧桂园
代理执行之后:rent
运行时就会在控制台上打印出来,这也是Spring AOP的核心
JDK动态代理类实现了InvocationHandler接口,重写的invoke方法。
JDK动态代理的基础是反射机制(method.invoke(对象,参数))Proxy.newProxyInstance()
2.2 基于Cglib的动态代理
基于Jdk的动态代理局限性在于代理的类必须要实现接口,而基于CGlib的动态代理则没有这个限制:
搭建CGlib环境我们首先要引入一个CGlib的jar包:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
此时我们不再需要接口,直接新建一个CGRoom类:
public class CGRoom {
public void rent(String roomName){
System.out.println("租了"+roomName);
}
}
CGlib实现动态代理的核心在于MethodInterceptor接口:
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理执行之前:"+method.getName());
Object object=methodProxy.invokeSuper(o,objects);
System.out.println("代理执行之后:"+method.getName());
return object;
}
这个接口只有一个intercept()方法,拦截被代理对象,这个方法有4个参数:
1)表示增强的对象,即实现这个接口类的一个对象;
2)表示要被拦截的方法;
3)表示要被拦截方法的参数;
4)表示要触发父类的方法对象;
最后生成代理类对象并输出执行结果
public static void main(String[] args) {
//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
Enhancer enhancer=new Enhancer();
//设置目标类的字节码文件
enhancer.setSuperclass(CGRoom.class);
//设置回调函数
enhancer.setCallback(new MyMethodInterceptor());
//创建代理对象
CGRoom proxy= (CGRoom) enhancer.create();
proxy.rent("碧桂园");
}
运行结果:
代理执行之前:rent
租了碧桂园
代理执行之后:rent
(三)总结
两种代理方式各有优劣,在使用方面,JDK动态代理只能基于接口进行实现,而CGLIb对代理的目标对象无限制,无需实现接口。
在依赖方面,Java原生支持JDK动态代理,而CGlib的实现还需要引入相关依赖包。