JavaJDK动态代理使用及实现原理分析
一、什么是代理?
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
代理模式UML图:
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。
二、Java动态代理类
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
(1)InterfaceInvocationHandler:该接口中仅定义了一个方法
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicobjectinvoke(Objectobj,Methodmethod,Object[]args)
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
(2)Proxy:该类即为动态代理类,其中主要包含以下内容:
protectedProxy(InvocationHandlerh):构造函数,用于给内部的h赋值。
staticClassgetProxyClass(ClassLoaderloader,Class[]interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
staticObjectnewProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
所谓DynamicProxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
在使用动态代理类时,我们必须实现InvocationHandler接口
通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。
动态代理步骤:
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)创建一个代理
4.通过代理调用方法
三、JDK的动态代理怎么使用?
1、需要动态代理的接口:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
packagejiankunking;
/
需要动态代理的接口
/
publicinterfaceSubject
{
/
你好
@paramname
@return
/
publicStringSayHello(Stringname);
/
再见
@return
/
publicStringSayGoodBye();
}
2、需要代理的实际对象
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
packagejiankunking;
/
实际对象
/
publicclassRealSubjectimplementsSubject
{
/
你好
@paramname
@return
/
publicStringSayHello(Stringname)
{
return"hello"+name;
}
/
再见
@return
/
publicStringSayGoodBye()
{
return"goodbye";
}
}
3、调用处理器实现类(有木有感觉这里就是传说中的AOP啊)
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
packagejiankunking;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
/
调用处理器实现类
每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象
/
publicclassInvocationHandlerImplimplementsInvocationHandler
{
/
这个就是我们要代理的真实对象
/
privateObjectsubject;
/
构造方法,给我们要代理的真实对象赋初值
@paramsubject
/
publicInvocationHandlerImpl(Objectsubject)
{
this.subject=subject;
}
/
该方法负责集中处理动态代理类上的所有方法调用。
调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
@paramproxy代理类实例
@parammethod被调用的方法对象
@paramargs调用参数
@return
@throwsThrowable
/
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable
{
//在代理真实对象前我们可以添加一些自己的操作
System.out.println("在调用之前,我要干点啥呢?");
System.out.println("Method:"+method);
//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
ObjectreturnValue=method.invoke(subject,args);
//在代理真实对象后我们也可以添加一些自己的操作
System.out.println("在调用之后,我要干点啥呢?");
returnreturnValue;
}
}
4、测试
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
packagejiankunking;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Proxy;
/
动态代理演示
/
publicclassDynamicProxyDemonstration
{
publicstaticvoidmain(String[]args)
{
//代理的真实对象
SubjectrealSubject=newRealSubject();
/
InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发
其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
/
InvocationHandlerhandler=newInvocationHandlerImpl(realSubject);
ClassLoaderloader=handler.getClass().getClassLoader();
Class[]interfaces=realSubject.getClass().getInterfaces();
/
该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
/
Subjectsubject=(Subject)Proxy.newProxyInstance(loader,interfaces,handler);
System.out.println("动态代理对象的类型:"+subject.getClass().getName());
Stringhello=subject.SayHello("jiankunking");
System.out.println(hello);
//Stringgoodbye=subject.SayGoodBye();
//System.out.println(goodbye);
}
}
5、输出结果如下:
四、动态代理怎么实现的?
从使用代码中可以看出,关键点在:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
Subjectsubject=(Subject)Proxy.newProxyInstance(loader,interfaces,handler);
通过跟踪提示代码可以看出:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用。
也就是说,当代码执行到:
subject.SayHello("jiankunking")这句话时,会自动调用InvocationHandlerImpl的invoke方法。这是为啥呢?
======================横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论=====================================
以下代码来自:JDK1.8.0_92
既然生成代理对象是用的Proxy类的静态方newProxyInstance,那么我们就去它的源码里看一下它到底都做了些什么?
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicstaticObjectnewProxyInstance(ClassLoaderloader,
Class>[]interfaces,
InvocationHandlerh)
throwsIllegalArgumentException
{
//检查h不为空,否则抛异常
Objects.requireNonNull(h);
finalClass>[]intfs=interfaces.clone();
finalSecurityManagersm=System.getSecurityManager();
if(sm!=null){
checkProxyAccess(Reflection.getCallerClass(),loader,intfs);
}
/
获得与指定类装载器和一组接口相关的代理类类型对象
/
Class>cl=getProxyClass0(loader,intfs);
/
通过反射获取构造函数对象并生成代理类实例
/
try{
if(sm!=null){
checkNewProxyPermission(Reflection.getCallerClass(),cl);
}
//获取代理对象的构造方法(也就是$Proxy0(InvocationHandlerh))
finalConstructor>cons=cl.getConstructor(constructorParams);
finalInvocationHandlerih=h;
if(!Modifier.isPublic(cl.getModifiers())){
AccessController.doPrivileged(newPrivilegedAction(){
publicVoidrun(){
cons.setAccessible(true);
returnnull;
}
});
}
//生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法
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);
}
}
我们再进去getProxyClass0方法看一下:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
/
Generateaproxyclass.MustcallthecheckProxyAccessmethod
toperformpermissionchecksbeforecallingthis.
/
privatestaticClass>getProxyClass0(ClassLoaderloader,
Class>...interfaces){
if(interfaces.length>65535){
thrownewIllegalArgumentException("interfacelimitexceeded");
}
//Iftheproxyclassdefinedbythegivenloaderimplementing
//thegiveninterfacesexists,thiswillsimplyreturnthecachedcopy;
//otherwise,itwillcreatetheproxyclassviatheProxyClassFactory
returnproxyClassCache.get(loader,interfaces);
}
真相还是没有来到,继续,看一下proxyClassCache
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
/
acacheofproxyclasses
/
privatestaticfinalWeakCache[],Class>>
proxyClassCache=newWeakCache<>(newKeyFactory(),newProxyClassFactory());
奥,原来用了一下缓存啊
那么它对应的get方法啥样呢?
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
/
Look-upthevaluethroughthecache.Thisalwaysevaluatesthe
{@codesubKeyFactory}functionandoptionallyevaluates
{@codevalueFactory}functionifthereisnoentryinthecacheforgiven
pairof(key,subKey)ortheentryhasalreadybeencleared.
@paramkeypossiblynullkey
@paramparameterparameterusedtogetherwithkeytocreatesub-keyand
value(shouldnotbenull)
@returnthecachedvalue(nevernull)
@throwsNullPointerExceptionif{@codeparameter}passedinor
{@codesub-key}calculatedby
{@codesubKeyFactory}or{@codevalue}
calculatedby{@codevalueFactory}isnull.
/
publicVget(Kkey,Pparameter){
Objects.requireNonNull(parameter);
expungeStaleEntries();
ObjectcacheKey=CacheKey.valueOf(key,refQueue);
//lazilyinstallthe2ndlevelvaluesMapfortheparticularcacheKey
ConcurrentMap |
|