微信搜索《Java鱼仔》,每天一个知识点不错过
(一)每天一个知识点
关于动态代理,你能说出动态代理的几种方式?
(二)详解
动态代理是指代理类不是写在代码中的,而是在代码运行过程中产生的,Java提供了两种方式来实现动态代理,分别是基于Jdk的动态代理和基于Cglib的动态代理。
2.1 基于Jdk的动态代理
首先我们来搭建一套实现动态代理的实现,以租房为例,我新建一个租房的接口:
publicinterfaceRoom { voidrent(); }
然后下一个实际的租房实现类
publicclassRealRoomimplementsRoom { privateStringroomname; publicRealRoom(Stringroomname) { this.roomname=roomname; } publicvoidrent() { System.out.println("租了"+roomname); } }
基于Jdk的动态代理核心在于InvocationHandler 接口,首先我们新建一个类ProxyHandler来实现这个InvocationHandler 接口,并实现里面的invoke方法
publicclassProxyHandlerimplementsInvocationHandler { Objectobject; publicProxyHandler(Objectobject) { this.object=object; } //proxy 代理对象//method 要实现的方法//args 方法的参数 publicObjectinvoke(Objectproxy, Methodmethod, Object[] args) throwsThrowable { System.out.println("代理执行之前:"+method.getName()); Objectinvoke=method.invoke(object, args); System.out.println("代理执行之后:"+method.getName()); returninvoke; } }
InvocationHandler 是一个接口,每个代理的实例都有一个与之关联的
InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类invoke,由它实现处理内容。
接下来写一个方法来实现动态代理
publicstaticvoidmain(String[] args) { Roomroom=newRealRoom("碧桂园"); //obj.getClass().getClassLoader()类加载器//obj.getClass().getInterfaces() 目标类实现的接口//InvocationHandler对象InvocationHandlerinvocationHandler=newProxyHandler(room); RoomproxyRoom= (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类:
publicclassCGRoom { publicvoidrent(StringroomName){ System.out.println("租了"+roomName); } }
CGlib实现动态代理的核心在于MethodInterceptor接口:
publicclassMyMethodInterceptorimplementsMethodInterceptor { publicObjectintercept(Objecto, Methodmethod, Object[] objects, MethodProxymethodProxy) throwsThrowable { System.out.println("代理执行之前:"+method.getName()); Objectobject=methodProxy.invokeSuper(o,objects); System.out.println("代理执行之后:"+method.getName()); returnobject; }
这个接口只有一个intercept()方法,拦截被代理对象,这个方法有4个参数:
1)表示增强的对象,即实现这个接口类的一个对象;
2)表示要被拦截的方法;
3)表示要被拦截方法的参数;
4)表示要触发父类的方法对象;
最后生成代理类对象并输出执行结果
publicstaticvoidmain(String[] args) { //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数Enhancerenhancer=newEnhancer(); //设置目标类的字节码文件enhancer.setSuperclass(CGRoom.class); //设置回调函数enhancer.setCallback(newMyMethodInterceptor()); //创建代理对象CGRoomproxy= (CGRoom) enhancer.create(); proxy.rent("碧桂园"); }
运行结果:
代理执行之前:rent租了碧桂园代理执行之后:rent
(三)总结
两种代理方式各有优劣,在使用方面,JDK动态代理只能基于接口进行实现,而CGLIb对代理的目标对象无限制,无需实现接口。
在依赖方面,Java原生支持JDK动态代理,而CGlib的实现还需要引入相关依赖包。