配色: 字号:
java动态代理原理及解析
2016-09-08 | 阅:  转:  |  分享 
  
java动态代理原理及解析

代理:设计模式



代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。





通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。



java动态代理的类和接口(jdk1.6源码)



1,java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。



//方法1:该方法用于获取指定代理对象所关联的调用处理器

publicstaticInvocationHandlergetInvocationHandler(Objectproxy)



//方法2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象

publicstaticClassgetProxyClass(ClassLoaderloader,

Class...interfaces)



//方法3:该方法用于判断指定类对象是否是一个动态代理类

publicstaticbooleanisProxyClass(Classcl)



//方法4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例

publicstaticObjectnewProxyInstance(ClassLoaderloader,

Class[]interfaces,InvocationHandlerh)



2,java.lang.reflect.InvocationHandler:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问。



/

该方法负责集中处理动态代理类上的所有方法调用。

第一个参数既是代理类实例,

第二个参数是被调用的方法对象

第三个方法是调用参数。

调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行

/

publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)

throwsThrowable;



3,java.lang.ClassLoader:类装载器类,将类的字节码装载到Java虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由JVM在运行时动态生成的而非预存在于任何一个.class文件中。

每次生成动态代理类对象时都需要指定一个类装载器对象:newProxyInstance()方法第一个参数



动态代理机制



java动态代理创建对象的过程为如下步骤:

1,通过实现InvocationHandler接口创建自己的调用处理器;



//InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发

//其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用

InvocationHandlerhandler=newInvocationHandlerImpl(..);

1

2

3

2,通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类;



//通过Proxy为包括Interface接口在内的一组接口动态创建代理类的类对象

Classclazz=Proxy.getProxyClass(classLoader,newClass[]{Interface.class,...});

1

2

3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;



//通过反射从生成的类对象获得构造函数对象

Constructorconstructor=clazz.getConstructor(newClass[]{InvocationHandler.class});

1

2

4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。



//通过构造函数对象创建动态代理类实例

InterfaceProxy=(Interface)constructor.newInstance(newObject[]{handler});

1

2

为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。



//InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发

InvocationHandlerhandler=newInvocationHandlerImpl(..);



//通过Proxy直接创建动态代理类实例

Interfaceproxy=(Interface)Proxy.newProxyInstance(classLoader,

newClass[]{Interface.class},

handler);



动态代理的注意点:

1,包:代理接口是public,则代理类被定义在顶层包(package为空),否则(default),代理类被定义在该接口所在包,





2,生成的代理类为publicfinal,不能被继承,



3,类名:格式是“$ProxyN”,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类,要注意,对于同一组接口(接口的排列顺序也相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象。提高了效率。



4,类继承关系:



Proxy类是它的父类,这个规则适用于所有由Proxy创建的动态代理类。(也算是java动态代理的一处缺陷,java不支持多继承,所以无法实现对class的动态代理,只能对于Interface的代理)而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。



5,代理类的根类java.lang.Object中有三个方法也同样会被分派到调用处理器的invoke方法执行,它们是hashCode,equals和toString,

代码在反编译中



一个动态代理的demo



importjava.lang.reflect.InvocationHandler;

importjava.lang.reflect.Method;

importjava.lang.reflect.Proxy;



publicclassHelloServiceProxyimplementsInvocationHandler{





privateObjecttarget;

/

绑定委托对象并返回一个【代理占位】

@paramtarget真实对象

@return代理对象【占位】

/

publicObjectbind(Objecttarget,Class[]interfaces){

this.target=target;

//取得代理对象

returnProxy.newProxyInstance(target.getClass().getClassLoader(),

target.getClawww.shanxiwang.netss().getInterfaces(),this);

}



@Override

/

同过代理对象调用方法首先进入这个方法.

@paramproxy--代理对象

@parammethod--方法,被调用方法.

@paramargs--方法的参数

/

publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{

System.err.println("############我是JDK动态代理################");

Objectresult=null;

//反射方法前调用

System.err.println("我准备说hello。");

//反射执行方法相当于调用target.sayHelllo;

result=method.invoke(target,args);

//反射方法后调用.

System.err.println("我说过hello了");

returnresult;

}

}



其中,bind方法中的newProxyInstanc方法,就是生成一个代理对象,第一个参数是类加载器,第二个参数是真实委托对象所实现的的接口(代理对象挂在那个接口下),第三个参数this代表当前HelloServiceProxy类,换句话说是使用HelloServiceProxy作为对象的代理。



invoke方法有三个参数:第一个proxy是代理对象,第二个是当前调用那个方法,第三个是方法的参数。



publicclassProxyTest{



publicstaticvoidmain(String[]args){

HelloServiceProxyproxy=newHelloServiceProxy();

HelloServiceservice=newHelloServiceImpl();

//绑定代理对象。

service=(HelloService)proxy.bind(service,newClass[]{HelloService.class});

//这里service经过绑定,就会进入invoke方法里面了。

service.sayHello("张三");

}

}



测试结果:



############我是JDK动态代理################

我准备说hello。

hello张三

我说过hello了

1

2

3

4

源码跟踪



Proxy类



//映射表:用于维护类装载器对象到其对应的代理类缓存

privatestaticMaploaderToCache=newWeakHashMap();



//标记:用于标记一个动态代理类正在被创建中

privatestaticObjectpendingGenerationMarker=newObject();



//同步表:记录已经被创建的动态代理类类型,主要被方法isProxyClass进行相关的判断

privatestaticMapproxyClasses=Collections.synchronizedMap(newWeakHashMap());



//关联的调用处理器引用

protectedInvocationHandlerh;



Proxy静态方法newProxyInstance



publicstaticObjectnewProxyInstance(ClassLoaderloader,

Class[]interfaces,

InvocationHandlerh)

throwsIllegalArgumentException{



//检查h不为空,否则抛异常

if(h==null){

thrownewNullPointerException();

}



//获得与制定类装载器和一组接口相关的代理类类型对象

/

Lookuporgeneratethedesignatedproxyclass.

/

Classcl=getProxyClass0(loader,interfaces);



//通过反射获取构造函数对象并生成代理类实例

/

Invokeitsconstructorwiththedesignatedinvocationhandler.

/

try{

finalConstructorcons=cl.getConstructor(constructorParams);

finalInvocationHandlerih=h;

SecurityManagersm=System.getSecurityManager();

if(sm!=null&&ProxyAcctessHelper.needsNewInstanceCheck(cl)){

//createproxyinstancewithdoPrivilegeastheproxyclassmay

//implementnon-publicinterfacesthatrequiresaspecialpermission

returnAccessController.doPrivileged(newPrivilegedAction(){

publicObjectrun(){

returnnewInstance(cons,ih);

}

});

}else{

returnnewInstance(cons,ih);

}

}catch(NoSuchMethodExceptione){

thrownewInternalError(e.toString());

}

}



privatestaticObjectnewInstance(Constructorcons,InvocationHandlerh){

try{

returncons.newInstance(newObject[]{h});

}catch(IllegalAccessExceptione){

thrownewInternalError(e.toString());

}catch(InstantiationExceptione){

thrownewInternalError(e.toString());

}catch(InvocationTargetExceptione){

Throwablet=e.getCause();

if(tinstanceofRuntimeException){

throw(RuntimeException)t;

}else{

thrownewInternalError(t.toString());

}

}

}



动态代理真正的关键是在getProxyClass0方法,



getProxyClass0方法分析



方法分为四个步骤:

1,对这组接口进行一定程度的安全检查

检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的,还会检查确保是interface类型而不是class类型。





2,从loaderToCache映射表中获取以类装载器对象为关键字所对应的缓存表,如果不存在就创建一个新的缓存表并更新到loaderToCache。

loaderToCache存放键值对(接口名字列表,动态生成的代理类的类对象引用)。当代理类正在被创建时它会临时保存(接口名字列表,pendingGenerationMarker)。标记pendingGenerationMarke的作用是通知后续的同类请求(接口数组相同且组内接口排列顺序也相同)代理类正在被创建,请保持等待直至创建完成。



/

Findorcreatetheproxyclasscachefortheclassloader.

/

Mapcache;

synchronized(loaderToCache){

cache=(Map)loaderToCache.get(loader);

if(cache==null){

cache=newHashMap();

loaderToCache.put(loader,cache);

}

}

。。。。。

do{

//以接口名字列表作为关键字获得对应cache值

Objectvalue=cache.get(key);

if(valueinstanceofReference){

proxyClass=(Class)((Reference)value).get();

}

if(proxyClass!=null){

//如果已经创建,直接返回

returnproxyClass;

}elseif(value==pendingGenerationMarker){

//代理类正在被创建,保持等待

try{

cache.wait();

}catch(InterruptedExceptione){

}

//等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待

continue;

}else{

//标记代理类正在被创建

cache.put(key,pendingGenerationMarker);

//break跳出循环已进入创建过程

break;

}while(true);



3,动态创建代理类的class对象



/

Chooseanamefortheproxyclasstogenerate.

/

longnum;

synchronized(nextUniqueNumberLock){

num=nextUniqueNumber++;

}

StringproxyName=proxyPkg+proxyClassNamePrefix+num;

/

Verifythattheclassloaderhasn''talready

definedaclasswiththechosenname.

/



//动态地生成代理类的字节码数组

byte[]proxyClassFile=ProxyGenerator.generateProxyClass(

proxyName,interfaces);

try{

//动态地定义新生成的代理类

proxyClass=defineClass0(loader,proxyName,

proxyClassFile,0,proxyClassFile.length);

}catch(ClassFormatErrore){

/

AClassFormatErrorheremeansthat(barringbugsinthe

proxyclassgenerationcode)therewassomeother

invalidaspectoftheargumentssuppliedtotheproxy

classcreation(suchasvirtualmachinelimitations

exceeded).

/

thrownewIllegalArgumentException(e.toString());

}

//把生成的代理类的类对象记录进proxyClasses表

proxyClasses.put(proxyClass,null);



首先根据规则(接口public与否),生成代理类的名称,$ProxyN格式,然后动态生成代理类。

所有的代码生成的工作都由ProxyGenerator所完成了,该类在rt.jar中,需要反编译



publicstaticbyte[]generateProxyClass(finalStringname,

Class[]interfaces)

{

ProxyGeneratorgen=newProxyGenerator(name,interfaces);

//这里动态生成代理类的字节码,由于比较复杂就不进去看了

finalbyte[]classFile=gen.generateClassFile();



//如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上

if(saveGeneratedFiles){

java.security.AccessController.doPrivileged(

newjava.security.PrivilegedAction(){

publicVoidrun(){

try{

FileOutputStreamfile=

newFileOutputStream(dotToSlash(name)+".class");

file.write(classFile);

file.close();

returnnull;

}catch(IOExceptione){

thrownewInternalError(

"I/Oexceptionsavinggeneratedfile:"+e);

}

}

});

}



//返回代理类的字节码

returnclassFile;

}



4,代码生成过程进入结尾部分,根据结果更新缓存表,如果成功则将代理类的类对象引用更新进缓存表,否则清楚缓存表中对应关键值,最后唤醒所有可能的正在等待的线程。



finally{

/

Wemustcleanupthe"pendinggeneration"stateoftheproxy

classcacheentrysomehow.Ifaproxyclasswassuccessfully

generated,storeitinthecache(withaweakreference);

otherwise,removethereservedentry.Inallcases,notify

allwaitersonreservedentriesinthiscache.

/

synchronized(cache){

if(proxyClass!=null){

cache.put(key,newWeakReference(proxyClass));

}else{

cache.remove(key);

}

cache.notifyAll();

}

}

returnproxyClass;

InvocationHandler解析



代码参考:http://rejoy.iteye.com/blog/1627405

通过getProxyClass0方法中生成具体的class文件的过程,定义path,讲class文件写到指定的磁盘中,反编译生成的代理class文件。

发现在静态代码块中获取了的方法有:Object中的equals方法、Object中的hashCode方法、Object中toString方法,以及invoke的接口方法。



后语



至此,JDK是动态生成代理类,并通过调用解析器,执行接口实现的方法的原理已经一目了然。动态代理加上反射,是很多框架的基础。比如Spring的AOP机制,自定义前置后置通知等控制策略,以及mybatis中的运用反射和动态代理来实现插件技术等等。

献花(0)
+1
(本文系网络学习天...首藏)