Java动态代理静态代理实例分析
代理模式:为其他对象提供一种代理以控制某个对象的访问。用在:在某些情况下,一个客户不想或者不能直接访问另一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用,代理对象还可以完成它附加的操作。
例子:就像房东、租客、中介的关系。中介(代理对象)为房东(真实对象)出租房子,租客(客户)通过中介(代理对象)来找房子租房子,中介完成了租房以后可以收取中介费(附加操作)。
先看看静态代理模式,通过上面对代理模式的理解,可以了解到代理模式:即不直接通过new一个真实对象来调用方法,而是通过代理对象来调用一个方法,所以代理对象包含真实对象的引用。下面看一下代码
接口:Subject包含一个方法
packagecom.example.designpattern.proxy; publicinterfaceSubject{ voidrequest(); }
RealSubject类,实现了Subject接口,为了简单起见,方法简单的输出一句话:
packagecom.example.designpattern.proxy; publicclassRealSubjectimplementsSubject{ //真是角色实现了 publicvoidrequest(){ System.out.println("Fromrealsubject"); } }
代理类ProxySubject,也要实现Subject接口,实现Subject里面的方法,但是在这里里面是通过调用真实对象来实现的。
packagecom.example.designpattern.proxy; publicclassProxySubjectimplementsSubject{ privateRealSubjectrealSubject;//代理角色内部引用了真实角色 //代理角色实现目标动作 publicvoidrequest(){ this.preRequest();//在真实角色操作之前所附加的操作 if(realSubject==null){ realSubject=newRealSubject(); } realSubject.request();//真实角色所完成的事情 this.afterRequet();//在真实角色操作之后附加的操作 } //代理角色之前完成的动作 privatevoidpreRequest(){ System.out.println("prerequest"); } //代理角色之后完成的动作 privatevoidafterRequet(){ System.out.println("afterrequest"); } }
客户调用者
packagecom.example.designpattern.proxy; publicclassClient{ publicstaticvoidmain(String[]args){ ProxySubjectproxy=newProxySubject(); //通过代理对象来调用方法 proxy.request(); } }
静态代理:
可以运行一下这些代码哦,可以在Client类中看到,是通过代理ProxySubject的对象proxy来调用方法的,在代理类ProxySubject中,有一个真实对象的引用,在代理对象的中request()方法调用了真实对象的方法。这样的模式叫做代理模式。
优点是:
1.代理模式能将代理对象与真实对象被调用的目标对象分离。
2.一定程度上降低了系统的耦合度,扩展性好。
代理类中包含了对真实主题的引用,这样做也有缺点:
1.真实对象与代理类一一对应,增加真实类也要增加代理类,这样做会快速的增加类的数量,使得系统变得复杂。
2.设计代理以前真实主题必须事先存在,不太灵活。
采用动态代理可以解决以上问题,动态代理是相对于静态代理来说的。
可能你也会说怎么样实现动态创建实例,以前我们创建实例不都是通过new的方式来实现的吗?
Hellohi=newHello();
那么动态创建实例是由Java提供的功能,就不需要我们去new对象,他已经定义好了静态方法Proxy.newProxyInstance(),只要传入参数调用就可以。Java文档里面有哦,如图:
Java标准库提供了一种动态代理(DynamicProxy)的机制:可以在运行期动态创建某个interface的实例。
参数解释:
Proxy.newProxyInstance( ClassLoaderloader,//传入ClassLoader Class>[]interfaces,//传入要调用的接口的方法数组 InvocationHandlerh);//传入InvocationHandler的实例
下面看一下动态代理例子代码:
Subject接口
packagedesign.dynamicproxy; publicinterfaceSubject{ voidrequest(Stringstr); }
RealSubject类实现Subject接口
packagedesign.dynamicproxy; publicclassRealSubjectimplementsSubject{ @Override publicvoidrequest(Stringstr){ System.out.println("FromRealSubject!"+"args:"+str); } }
动态代理类DynamicSubject实现了InvocationHandler,重写invoke()方法
packagedesign.dynamicproxy; importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; /** *该代理类的内部属性时Object类型,实际使用时,使用该类的构造方法传递一个对象 *此外该类还实现了invoke()方法,该方法中的method.invoke()其实就是要调用被代理对象的要执行的方法 *方法参数是object,表示该方法从属于object对象,通过动态代理类,我们可以在执行真是对象的 *方法前后可以加入一些额外的方法 */ publicclassDynamicSubjectimplementsInvocationHandler{ //引入的类型是Object的,可以随便传入任何一个对象 privateObjectobject; publicDynamicSubject(Objectobject){ this.object=object; } @Override publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ System.out.println("beforecalling:"+method); //等价于realSubject的request()方法,如果这里不调用的话,不会调用Method对象中的方法 method.invoke(object,args); System.out.println("aftercalling:"+method); returnnull; } }
Client类
packagedesign.dynamicproxy; importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Proxy; publicclassClient{ publicstaticvoidmain(String[]args){ RealSubjectrealSubject=newRealSubject(); InvocationHandlerhandler=newDynamicSubject(realSubject); Class>classType=handler.getClass(); //下面的代码一次性生成代理 //动态生成了classcom.sun.proxy.$Proxy0的实例, Subjectsubject=(Subject)Proxy.newProxyInstance(classType.getClassLoader(),realSubject.getClass().getInterfaces(),handler); subject.request("eather"); System.out.println(subject.getClass()); //输出classcom.sun.proxy.$Proxy0,可以看到Proxy.newProxyInstance()是系统自动生成的实例 } }
在Client中可以看到,我们这里调用方法的是subject.request("eather");这个对象subject不是通过newDynamicSubject()生成的,而是Java内部写好的方法在运行时动态生成对象;可能有人说
InvocationHandlerhandler=newDynamicSubject(realSubject);
这里不是通过newnewDynamicSubject(realSubject);生成了一个对象吗?是的,但是它是InvocationHandler类型的,主要是传递一个InvocationHandler类型参数给Proxy.newProxyInstance();即最后一个参数。通过Client类的最后一句输出可以看到它是classcom.sun.proxy.$Proxy0,这是Java运行时生成的。
解决了静态代理的难题:1.真实对象与代理类一一对应,增加真实类也要增加代理类,这样做会快速的增加类的数量,使得系统变得复杂。为什么这么说呢,因为代理类引用的类型是Object的,可以随便传入任何一个对象,当真实类增加时,代理类不用增加,newDynamicSubject(object);new的时候把要传入的对象传进去即可。
下面是Proxy.newProxyInstance(ClassLoaderloader,Class>[]interfaces,InvocationHandlerh);这个方法的源码啦,可以看看,深入了解一下
publicstaticObjectnewProxyInstance(ClassLoaderloader, Class>[]interfaces, InvocationHandlerh) throwsIllegalArgumentException { Objects.requireNonNull(h); finalClass>[]intfs=interfaces.clone(); finalSecurityManagersm=System.getSecurityManager(); if(sm!=null){ checkProxyAccess(Reflection.getCallerClass(),loader,intfs); } /* *Lookuporgeneratethedesignatedproxyclass. 生成一个代理类对象 */ Class>cl=getProxyClass0(loader,intfs); /* *Invokeitsconstructorwiththedesignatedinvocationhandler. 使用指定的调用处理程序调用其构造函数。就是使用InvocationHandler实例调用【要调用方法的那个类】的构造方法 */ try{ if(sm!=null){ checkNewProxyPermission(Reflection.getCallerClass(),cl); } finalConstructor>cons=cl.getConstructor(constructorParams); finalInvocationHandlerih=h; if(!Modifier.isPublic(cl.getModifiers())){ AccessController.doPrivileged(newPrivilegedAction(){ publicVoidrun(){ cons.setAccessible(true); returnnull; } } ); } returncons.newInstance(newObject[]{ h } ); } catch(IllegalAccessException|InstantiationExceptione){ thrownewInternalError(e.toString(),e); } catch(InvocationTargetExceptione){ Throwablet=e.getCause(); if(tinstanceofRuntimeException){ throw(RuntimeException)t; }else{ thrownewInternalError(t.toString(),t); } } catch(NoSuchMethodExceptione){ thrownewInternalError(e.toString(),e); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。