Java 动态代理深入理解
要想了解Java动态代理,首先要了解什么叫做代理,熟悉设计模式的朋友一定知道在Gof总结的23种设计模式中,有一种叫做代理(Proxy)的对象结构型模式,动态代理中的代理,指的就是这种设计模式。
在我看来所谓的代理模式,和23种设计模式中的“装饰模式”是一个东西。23种设计模式中将它们作为两种模式,网上也有些文章讲这两种模式的异同,从细节来看,确实可以人为地区分这两种模式,但是抽象到一定高度后,我认为这两种模式是完全一样的。因此学会了代理模式,也就同时掌握了装饰模式。
代理模式
代理模式简单来说,就是对一个对象进行包装,包装后生成的对象具有和原对象一样的方法列表,但是每个方法都可以是被包装过的。
静态代理
让我们先来看一段代码:
packagecommon;
publicclassTest{
staticinterfaceSubject{
voidsayHi();
voidsayHello();
}
staticclassSubjectImplimplementsSubject{
@Override
publicvoidsayHi(){
System.out.println("hi");
}
@Override
publicvoidsayHello(){
System.out.println("hello");
}
}
staticclassSubjectImplProxyimplementsSubject{
privateSubjecttarget;
publicSubjectImplProxy(Subjecttarget){
this.target=target;
}
@Override
publicvoidsayHi(){
System.out.print("say:");
target.sayHi();
}
@Override
publicvoidsayHello(){
System.out.print("say:");
target.sayHello();
}
}
publicstaticvoidmain(String[]args){
Subjectsubject=newSubjectImpl();
SubjectsubjectProxy=newSubjectImplProxy(subject);
subjectProxy.sayHi();
subjectProxy.sayHello();
}
}
这段代码中首先定义了一个Subject接口,接口中有两个方法。
然后定义了SubjectImpl类实现Subject接口并实现其中的两个方法,到这里肯定是没问题的。
现在再定义一个SubjuectImplProxy类,也实现Subject接口。这个SubjectImplProxy类的作用是包装SubjectImpl类的实例,它的内部定义一个变量target来保存一个SubjectImpl的实例。SubjectImplProxy也实现了接口规定的两个方法,并且在它的实现版本中,都调用了SubjectImpl的实现,但是又添加了自己的处理逻辑。
相信这段代码不难理解,它通过对SubjectImpl进行包装,达到了给输出内容添加前缀的功能。这种代理方式叫做静态代理。
动态代理
从上面的演示中我们不难看出静态代理的缺点:我们对SubjectImpl的两个方法,是进行的相同的包装,但是却要在SubjectImplProxy里把相同的包装逻辑写两次,而且以后如果Subject接口再添加新的方法,SubjectImplProxy也必须要添加新的实现,尽管SubjectImplProxy对所有方法的包装可能都是一样的。
下面我把上面例子的静态代理改成动态代理,我们来看一下区别:
packagecommon;
importjava.lang.invoke.MethodHandle;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
publicclassTest{
staticinterfaceSubject{
voidsayHi();
voidsayHello();
}
staticclassSubjectImplimplementsSubject{
@Override
publicvoidsayHi(){
System.out.println("hi");
}
@Override
publicvoidsayHello(){
System.out.println("hello");
}
}
staticclassProxyInvocationHandlerimplementsInvocationHandler{
privateSubjecttarget;
publicProxyInvocationHandler(Subjecttarget){
this.target=target;
}
@Override
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
System.out.print("say:");
returnmethod.invoke(target,args);
}
}
publicstaticvoidmain(String[]args){
Subjectsubject=newSubjectImpl();
SubjectsubjectProxy=(Subject)Proxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),newProxyInvocationHandler(subject));
subjectProxy.sayHi();
subjectProxy.sayHello();
}
}
只看main方法的话,只有第二行和之前的静态代理不同,同样是生成一个subjectProxy代理对象,只是生成的代码不同了。静态代理是直接new一个SubjectImplProxy的实例,而动态代理则调用了java.lang.reflect.Proxy.newProxyInstance()方法,我们来看一下这个方法的源码:
publicstaticObjectnewProxyInstance(ClassLoaderloader,
Class>[]interfaces,
InvocationHandlerh)
throwsIllegalArgumentException
{
if(h==null){
thrownewNullPointerException();
}
/*
*Lookuporgeneratethedesignatedproxyclass.
*/
Class>cl=getProxyClass(loader,interfaces);//获取代理类的Class
/*
*Invokeitsconstructorwiththedesignatedinvocationhandler.
*/
try{
Constructorcons=cl.getConstructor(constructorParams);//constructorParams是写死的:{InvocationHandler.class},上边返回的代理类Class一定是extendsProxy的,而Proxy有一个参数为InvocationHandler的构造函数
returncons.newInstance(newObject[]{h});//这里通过构造函数将我们自己定义的InvocationHandler的子类传到代理类的实例里,当我们调用代理类的任何方法时,实际上都会调用我们定义的InvocationHandler子类重写的invoke()函数
}catch(NoSuchMethodExceptione){
thrownewInternalError(e.toString());
}catch(IllegalAccessExceptione){
thrownewInternalError(e.toString());
}catch(InstantiationExceptione){
thrownewInternalError(e.toString());
}catch(InvocationTargetExceptione){
thrownewInternalError(e.toString());
}
}
上面的Class>cl=getProxyClass(loader,interfaces); 调用的getProxyClass方法:
publicstaticClass>getProxyClass(ClassLoaderloader,
Class>...interfaces)
throwsIllegalArgumentException
{
if(interfaces.length>65535){//因为在class文件中,一个类保存的接口数量是用2个字节来表示的,因此java中一个类最多可以实现65535个接口
thrownewIllegalArgumentException("interfacelimitexceeded");
}
Class>proxyClass=null;
/*collectinterfacenamestouseaskeyforproxyclasscache*/
String[]interfaceNames=newString[interfaces.length];
//fordetectingduplicates
Set>interfaceSet=newHashSet<>();
//验证interfaces里的接口是否能被类加载器加载,是否是接口,是否有重复的
for(inti=0;iinterfaceClass=null;
try{
interfaceClass=Class.forName(interfaceName,false,loader);
}catch(ClassNotFoundExceptione){
}
if(interfaceClass!=interfaces[i]){
thrownewIllegalArgumentException(
interfaces[i]+"isnotvisiblefromclassloader");
}
/*
*VerifythattheClassobjectactuallyrepresentsan
*interface.
*/
if(!interfaceClass.isInterface()){
thrownewIllegalArgumentException(
interfaceClass.getName()+"isnotaninterface");
}
/*
*Verifythatthisinterfaceisnotaduplicate.
*/
if(interfaceSet.contains(interfaceClass)){
thrownewIllegalArgumentException(
"repeatedinterface:"+interfaceClass.getName());
}
interfaceSet.add(interfaceClass);
interfaceNames[i]=interfaceName;
}
/*
*Usingstringrepresentationsoftheproxyinterfacesas
*keysintheproxyclasscache(insteadoftheirClass
*objects)issufficientbecausewerequiretheproxy
*interfacestoberesolvablebynamethroughthesupplied
*classloader,andithastheadvantagethatusingastring
*representationofaclassmakesforanimplicitweak
*referencetotheclass.
*/
Listkey=Arrays.asList(interfaceNames);//使用interfaces列表作为key缓存在cache里,也就是实现了相同interfaces的代理类只会创建加载一次
/*
*Findorcreatetheproxyclasscachefortheclassloader.
*/
Map,Object>cache;
synchronized(loaderToCache){
cache=loaderToCache.get(loader);
if(cache==null){
cache=newHashMap<>();
loaderToCache.put(loader,cache);
}
/*
*Thismappingwillremainvalidforthedurationofthis
*method,withoutfurthersynchronization,becausethemapping
*willonlyberemovediftheclassloaderbecomesunreachable.
*/
}
/*
*Lookupthelistofinterfacesintheproxyclasscacheusing
*thekey.Thislookupwillresultinoneofthreepossible
*kindsofvalues:
*null,ifthereiscurrentlynoproxyclassforthelistof
*interfacesintheclassloader,
*thependingGenerationMarkerobject,ifaproxyclassforthe
*listofinterfacesiscurrentlybeinggenerated,
*oraweakreferencetoaClassobject,ifaproxyclassfor
*thelistofinterfaceshasalreadybeengenerated.
*/
//看看缓存里有没有,如果有就直接取出来然后return,否则判断根据pendingGenerationMarker判断是否有其它线程正在生成当前的代理类,如果有则cache.wait()等待,如果没有则创建。
synchronized(cache){
/*
*Notethatweneednotworryaboutreapingthecachefor
*entrieswithclearedweakreferencesbecauseifaproxyclass
*hasbeengarbagecollected,itsclassloaderwillhavebeen
*garbagecollectedaswell,sotheentirecachewillbereaped
*fromtheloaderToCachemap.
*/
do{
Objectvalue=cache.get(key);
if(valueinstanceofReference){
proxyClass=(Class>)((Reference)value).get();
}
if(proxyClass!=null){
//proxyclassalreadygenerated:returnit
returnproxyClass;
}elseif(value==pendingGenerationMarker){
//proxyclassbeinggenerated:waitforit
try{
cache.wait();
}catch(InterruptedExceptione){
/*
*Theclassgenerationthatwearewaitingforshould
*takeasmall,boundedtime,sowecansafelyignore
*threadinterruptshere.
*/
}
continue;
}else{
/*
*Noproxyclassforthislistofinterfaceshasbeen
*generatedorisbeinggenerated,sowewillgoand
*generateitnow.Markitaspendinggeneration.
*/
cache.put(key,pendingGenerationMarker);
break;
}
}while(true);
}
//确认要生成的代理类所属的包,如果interfaces里所有接口都是public的,代理类所属包就是默认包;如果有interface不是public,那么所有不是public的interface必须在一个包里否则报错。
try{
StringproxyPkg=null;//packagetodefineproxyclassin
/*
*Recordthepackageofanon-publicproxyinterfacesothatthe
*proxyclasswillbedefinedinthesamepackage.Verifythat
*allnon-publicproxyinterfacesareinthesamepackage.
*/
for(inti=0;i>(proxyClass));
}else{
cache.remove(key);
}
cache.notifyAll();
}
}
returnproxyClass;//最后返回代理类Class
}
到这里,我们已经把动态代理的java源代码都解析完了,现在思路就很清晰了:
Proxy.newProxyInstance(ClassLoaderloader,Class>[]interfaces,InvocationHandlerh)方法简单来说执行了以下操作:
1.生成一个实现了参数interfaces里所有接口且继承了Proxy的代理类的字节码,然后用参数里的classLoader加载这个代理类。
2.使用代理类父类的构造函数Proxy(InvocationHandlerh)来创造一个代理类的实例,将我们自定义的InvocationHandler的子类传入。
3.返回这个代理类实例,因为我们构造的代理类实现了interfaces(也就是我们程序中传入的subject.getClass().getInterfaces())里的所有接口,因此返回的代理类可以强转成Subject类型来调用接口中定义的方法。
现在我们知道了用Proxy.newProxyInstance()返回的subjectProxy可以成功强转成Subject类型来调用接口中定义的方法了,那么在调用方法后,代理类实例怎么进行处理的呢,这就需要看一下代理类的源码了。但是代理类是程序动态生成字节码加载的,怎么看源码呢?没关系,可以在main方法中加入System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”),这样就会把生成的代理类Class文件保存在本地磁盘上,然后再反编译可以得到代理类的源码:
packagecommon;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
importjava.lang.reflect.UndeclaredThrowableException;
publicfinalclass$Proxy0extendsProxy
implementsTest.Subject
{
privatestaticMethodm4;
privatestaticMethodm1;
privatestaticMethodm3;
privatestaticMethodm0;
privatestaticMethodm2;
static
{
try{
m4=Class.forName("Test$Subject").getMethod("sayHello",newClass[0]);
m1=Class.forName("java.lang.Object").getMethod("equals",newClass[]{Class.forName("java.lang.Object")});
m3=Class.forName("Test$Subject").getMethod("sayHi",newClass[0]);
m0=Class.forName("java.lang.Object").getMethod("hashCode",newClass[0]);
m2=Class.forName("java.lang.Object").getMethod("toString",newClass[0]);
}catch(Exceptione){
thrownewRuntimeException(e);
}
}
public$Proxy0(InvocationHandlerparamInvocationHandler)
{
super(paramInvocationHandler);
}
publicfinalvoidsayHello()
{
try
{
this.h.invoke(this,m4,null);
return;
}
catch(RuntimeExceptionlocalRuntimeException)
{
throwlocalRuntimeException;
}
catch(ThrowablelocalThrowable)
{
thrownewUndeclaredThrowableException(localThrowable);
}
}
publicfinalbooleanequals(ObjectparamObject)
{
try
{
return((Boolean)this.h.invoke(this,m1,newObject[]{paramObject})).booleanValue();
}
catch(RuntimeExceptionlocalRuntimeException)
{
throwlocalRuntimeException;
}
catch(ThrowablelocalThrowable)
{
thrownewUndeclaredThrowableException(localThrowable);
}
}
publicfinalvoidsayHi()
{
try
{
this.h.invoke(this,m3,null);
return;
}
catch(RuntimeExceptionlocalRuntimeException)
{
throwlocalRuntimeException;
}
catch(ThrowablelocalThrowable)
{
thrownewUndeclaredThrowableException(localThrowable);
}
}
publicfinalinthashCode()
{
try
{
return((Integer)this.h.invoke(this,m0,null)).intValue();
}
catch(RuntimeExceptionlocalRuntimeException)
{
throwlocalRuntimeException;
}
catch(ThrowablelocalThrowable)
{
thrownewUndeclaredThrowableException(localThrowable);
}
}
publicfinalStringtoString()
{
try
{
return(String)this.h.invoke(this,m2,null);
}
catch(RuntimeExceptionlocalRuntimeException)
{
throwlocalRuntimeException;
}
catch(ThrowablelocalThrowable)
{
thrownewUndeclaredThrowableException(localThrowable);
}
}
}
我们可以看到代理类内部实现比较简单,在调用每个代理类每个方法的时候,都用反射去调h的invoke方法(也就是我们自定义的InvocationHandler的子类中重写的invoke方法),用参数传递了代理类实例、接口方法、调用参数列表,这样我们在重写的invoke方法中就可以实现对所有方法的统一包装了。
总结
动态代理相对于静态代理在使用上的优点主要是能够对一个对象的所有方法进行统一包装,而且后期被代理的类添加方法的时候动态代理类不需要改动。
缺点是要求被代理的类必须实现了接口,因为动态代理类在实现的时候继承了Proxy类,java不支持多继承,因此动态代理类只能根据接口来定义方法。
最后动态代理之所以叫做动态代理是因为java在实现动态代理的时候,动态代理类是在运行时动态生成和加载的,相对的,静态代理类和其他普通类一下,在类加载阶段就加载了。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!