java中的动态代理(jdk和Cglib)实现详解
作者:自律的kkk
动态代理
一、JDK动态代理
准备
JDK动态代理只能代理接口,所以我们需要先准备一个接口,以及一个接口的实现类
// 需要代理的接口 public interface InvokeInterface { void test1(String test,int[] ints); String test3(String test); } //代理接口的实现类 public class InvokeBean implements InvokeInterface{ public void test1(String test,int[] ints){ System.out.println("代理实现方法1 :"+test + JSON.toJSON(ints)); } public String test3(String test){ System.out.println("代理实现方法1 :"+test); return test; } }
有了代理的目标对象了,我们自然还需要一个处理方法,毕竟我们需要实现额外的逻辑呀,不然代理干嘛?对吧,这个处理方法可不能乱写,需要实现InvocationHandler接口,里面有个invoke方法,就是给我们自己写逻辑的地方,我们就可以对方法执行前、后填充自己的逻辑了
注意: 这个类实例化的时候传了一个Object参数,这个就是上面的实现类,为什么要传进来?因为一个接口是可以有多个实现类的,你不传一个具体的实现类,我怎么知道要执行哪个实现类里面的方法呢?
public class DemoInvokeHandler implements InvocationHandler { private Object object; public DemoInvokeHandler(Object object){ this.object=object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 所以可以在这里 方法执行前写逻辑 System.out.println("方法执行前执行"); // 这个就是我们原本的方法执行 Object invoke=method.invoke(object,args); // 所以可以在这里 方法执行后写逻辑 System.out.println("方法执行后执行"); return invoke; } }
像以上这样呢,整个接口中的方法都会被代理,如果我们只想代理某个方法呢?就需要对方法判断一下如:
这里只是对方法名过滤了,还可以结合注解、参数等过滤
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("test1")){ // 所以可以在这里 方法执行前写逻辑 System.out.println("方法执行前执行"); // 这个就是我们原本的方法执行 Object invoke=method.invoke(object,args); // 所以可以在这里 方法执行后写逻辑 System.out.println("方法执行后执行"); return invoke; }else { return method.invoke(object,args); } }
使用
以上我们的准备工作就完成了,我们只需要在实际使用中,获取代理对象,执行代理对象即可,通过
Proxy.newProxyInstance获取代理对象,有三个传参:
- 类加载器
- 被代理的接口
- 需要做的处理类(也就是实现了InvocationHandler的类)
// 获取代理对象 InvokeInterface invokeBean = (InvokeInterface) Proxy.newProxyInstance(InvokeBean.class.getClassLoader(), new Class[]{InvokeInterface.class}, new DemoInvokeHandler(new InvokeBean())); invokeBean.test1("test1",new int[]{1,2}); String test3 = invokeBean.test3("test3"); System.out.println(test3);
二、Cglib动态代理
准备
这个我们需要先导入依赖包
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency>
Cglib和JDK实现起来差不多,但是Cglib代理的是类,所以我们需要先准备个目标类
public class CglibTarget { public void test(){ System.out.println("test 本方法执行"); } }
同样的还需要自己的处理方法,这个要实现的接口就不一样。是Callback接口,而该接口有几种:
我们只看一下MethodInterceptor和InvocationHandler两种,其他毕竟用得少:
InvocationHandler:用法和JDK动态代理一样,参考JDK的即可
MethodInterceptor:
注意: 是net.sf.cglib.proxy.MethodInterceptor包下的,同时这个处理类实例化的时候就不需要再传参了
public class CglibProxy implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { // 所以可以在这里 方法执行前写逻辑 System.out.println("方法执行前执行"); // 这个就是我们原本的方法执行 Object o1 = methodProxy.invokeSuper(o, objects); // 所以可以在这里 方法执行后写逻辑 System.out.println("方法执行后执行"); return o1; } }
使用
Cglib的使用同样是需要先生成一个代理对象,一般是靠Enhancer.create() 方法,但该方法有几个重载,我们一个一个介绍
先是最简单也是最常用的
Enhancer.create(Class type, Callback callback):
- type: 需要代理的类
- callback:处理方法类
// MethodInterceptor 方式 CglibTarget cglibTarget = (CglibTarget) Enhancer.create(CglibTarget.class, new CglibProxy()); cglibTarget.test(); // InvocationHandler 方式 与JDK一样,不需要类加载器了 Interface cglibTarget = (Interface) Enhancer.create(Interface.class, new CglibProxy(new 实现类)); cglibTarget.test();
结果:
####Enhancer.create((Class superclass, Class[] interfaces, Callback callback)):
- superclass: 需要代理的类
- interfaces:需要实现的接口
- callback:处理方法类
这个就比上面那个更厉害了,上面那个只是代理原有类里面的方法,这个可以对代理原有的类帮他实现接口,并实现接口逻辑,这等于一个类凭空多了几个方法出来,怎么用?
上述的目标类不需要改,处理方法需要改一下,然后新建一个接口:
// 我们新建一个接口不需要任何实现类 public interface TestTarget { public void test1(); } // 修改处理方法 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { // 这里新增对上面接口里面方法的处理,其他的不需要变 if(method.getName().equals("test1")){ // 这里可以实现那个接口方法的逻辑 System.out.println("这里写实现那个接口的逻辑方法"); return "我是那个接口返回的"; }else { // 所以可以在这里 方法执行前写逻辑 System.out.println("方法执行前执行"); // 这个就是我们原本的方法执行 Object o1 = methodProxy.invokeSuper(o, objects); // 所以可以在这里 方法执行后写逻辑 System.out.println("方法执行后执行"); return o1; } }
调用:
// 先用接口生成器 对我们要实现的接口做一个处理 InterfaceMaker interfaceMaker=new InterfaceMaker(); interfaceMaker.add(TestTarget.class); Class aClass = interfaceMaker.create(); // 然后我们就可以正常的传参调用了 CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibProxy()); // 类中方法正常调用 o.test(); // 接口方法调用需要用反射 因为毕竟那接口的方法不存在对象方法里面 Method test1 = o.getClass().getMethod("test1"); test1.invoke(o);
结果:
Enhancer. create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks)
- superclass: 需要代理的类
- interfaces:需要实现的接口
- filter: 这个就是用来选择处理类的,毕竟处理类有多个了
- callbacks:处理方法类数组(处理类可以有多个)
为了演示效果,我们新建一个处理类CglibProxyOther,上面的都不需要变
public class CglibProxyOther implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if(method.getName().equals("test1")){ System.out.println("新建处理类的逻辑方法"); return "我是那个新建处理类返回的"; }else { // 所以可以在这里 方法执行前写逻辑 System.out.println("新建处理类——方法执行前执行"); // 这个就是我们原本的方法执行 Object o1 = methodProxy.invokeSuper(o, objects); // 所以可以在这里 方法执行后写逻辑 System.out.println("新建处理类——方法执行后执行"); return o1; } } }
然后新增一个过滤选择器filter:
实现CallbackFilter接口即可,返回值是处理类数组的下标(我这里用方面名称来选择,实际还可以用其他)
public class CglibFilter implements CallbackFilter { @Override public int accept(Method method) { // test方法用下标为0的处理类 if(method.getName().equals("test")){ return 0; } // 其他方法用下标为1的处理类 return 1; } }
调用:
InterfaceMaker interfaceMaker=new InterfaceMaker(); interfaceMaker.add(TestTarget.class); Class aClass = interfaceMaker.create(); CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibFilter(),new Callback[]{new CglibProxy(),new CglibProxyOther()}); o.test(); Method test1 = o.getClass().getMethod("test1"); test1.invoke(o);
结果: 很明显的看到test1方法走了第二个处理类
至此重载方法全介绍完毕!
三、总结对比
- 拓展性:要是考虑使用角度Cglib无疑是更好的,因为JDK只能代理接口
- 原理:JDK代理是利用反射机制生成匿名类,调用也是通过反射来调用 Cglib是采用字节码技术,通过修改字节码生成子类
- 效率:JDK创建对象效率较高,但执行较慢,Cglib创建对象效率低,但执行较快
- 局限性: Cglib需要额外导入第三方包,而Jdk代理不需要,但JDK局限于代理接口
到底用什么相信大家有选择了,如果没特殊需求,就直接JDK得了,有特殊需求不用Cglib,JDK能满足吗?至于效率,这种效率的差距都体现在一定量往上的程度,没到这个量无需考虑太多!
到此这篇关于java中的动态代理(jdk和Cglib)实现详解的文章就介绍到这了,更多相关java 动态代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!