您的位置:首页 > 编程语言 > Java开发

Java动态代理的理解

2018-08-23 09:08 483 查看

代码内容:

https://github.com/cjy513203427/Java_Advanced_Knowledge/tree/master/src/com/advance/dynamic_proxy

Subject接口

packagecom.advance.dynamic_proxy;
/**
*Createdbyhaseeon2018/8/22.
*/
publicinterfaceSubject
{
publicvoidrent();

publicvoidhello(Stringstr);
}


RealSubjec类t实现Subject

packagecom.advance.dynamic_proxy;/**
*Createdbyhaseeon2018/8/22.
*/

/**
*@Auther:谷天乐
*@Date:2018/8/2219:35
*@Description:
*/
publicclassRealSubjectimplementsSubject
{
@Override
publicvoidrent()
{
System.out.println("Iwanttorentmyhouse");
}

@Override
publicvoidhello(Stringstr)
{
System.out.println("hello:"+str);
}
}


DynamicProxy实现InvocationHandler,必须要实现InvocationHandler类

packagecom.advance.dynamic_proxy;/**
*Createdbyhaseeon2018/8/22.
*/

importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;

/**
*@Auther:谷天乐
*@Date:2018/8/2219:36
*@Description:
*/
publicclassDynamicProxyimplementsInvocationHandler
{
// 这个就是我们要代理的真实对象
privateObjectsubject;

//构造方法,给我们要代理的真实对象赋初值
publicDynamicProxy(Objectsubject)
{
this.subject=subject;
}

@Override
publicObjectinvoke(Objectobject,Methodmethod,Object[]args)
throwsThrowable
{
//  在代理真实对象前我们可以添加一些自己的操作
System.out.println("beforerenthouse");

System.out.println("Method:"+method);

//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
method.invoke(subject,args);

//  在代理真实对象后我们也可以添加一些自己的操作
System.out.println("afterrenthouse");

returnnull;
}

}


启动类

packagecom.advance.dynamic_proxy;/**
*Createdbyhaseeon2018/8/22.
*/

importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Proxy;

/**
*@Auther:谷天乐
*@Date:2018/8/2219:36
*@Description:
*/
publicclassClient
{
publicstaticvoidmain(String[]args)
{
//我们要代理的真实对象
SubjectrealSubject=newRealSubject();

//我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandlerhandler=newDynamicProxy(realSubject);

/*
*通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
*第一个参数handler.getClass().getClassLoader(),我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
*第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
*第三个参数handler,我们这里将这个代理对象关联到了上方的InvocationHandler这个对象上
*/
Subjectsubject=(Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(),realSubject
.getClass().getInterfaces(),handler);

System.out.println(subject.getClass().getName());
subject.rent();
subject.hello("world");
}
}


原理:

通过代理类关联到InvocationHandler中的invoke方法调用了真实对象的方法,而不是直接调用的

我们可以对代理的方法前后自由的增加操作

先看InvocationHandler是什么东西

/**
*{@codeInvocationHandler}istheinterfaceimplementedby
*the<i>invocationhandler</i>ofaproxyinstance.

*InvocationHandler是一个被代理实例的调用程序实现的接口

*<p>Eachproxyinstancehasanassociatedinvocationhandler.
*Whenamethodisinvokedonaproxyinstance,themethod
*invocationisencodedanddispatchedtothe{@codeinvoke}
*methodofitsinvocationhandler.

*每个代理实例都有相关联的调用处理程序
  当一个方法被代理实例调用时,调用方法会被编码并分派它的调用程序的调用方法

*@authorPeterJones
*@seeProxy
*@since1.3
*/


再看InvocationHandler中唯一一个方法invoke

/**
*Processesamethodinvocationonaproxyinstanceandreturns
*theresult.Thismethodwillbeinvokedonaninvocationhandler
*whenamethodisinvokedonaproxyinstancethatitis
*associatedwith.
*
*@paramproxytheproxyinstancethatthemethodwasinvokedon

*参数proxy是指方法被调用的代理实例

*@parammethodthe{@codeMethod}instancecorrespondingto
*theinterfacemethodinvokedontheproxyinstance.Thedeclaring
*classofthe{@codeMethod}objectwillbetheinterfacethat
*themethodwasdeclaredin,whichmaybeasuperinterfaceofthe
*proxyinterfacethattheproxyclassinheritsthemethodthrough.

*参数method是一个实例,它就是调用在代理实例上的接口方法。声明的
    方法对象类是该方法声明的接口,这个接口是所有继承当前method的代理接口的父接口

*@paramargsanarrayofobjectscontainingthevaluesofthe
*argumentspassedinthemethodinvocationontheproxyinstance,
*or{@codenull}ifinterfacemethodtakesnoarguments.
*Argumentsofprimitivetypesarewrappedininstancesofthe
*appropriateprimitivewrapperclass,suchas
*{@codejava.lang.Integer}or{@codejava.lang.Boolean}.

*参数args是包含了代理方法调用中传输的对象数组参数。
    或者这个接口没有参数。
    原始类型的参数被打包在合适的包装类中,如Integer或者Boolean
    意思就是int会包装成Integer...

*/

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



我们真正执行rent()和hello(Stringstr)方法的地方在DynamicProxy类中

//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
method.invoke(subject,args);


声明一个真实对象,InvocationHandler引用子类(在C++中接口实际就是一个父类,所以我这么说)传入真实对象

//我们要代理的真实对象
SubjectrealSubject=newRealSubject();

//我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandlerhandler=newDynamicProxy(realSubject);


Proxy的newProxyInstance()方法

*@paramloadertheclassloadertodefinetheproxyclass
    loader是加载类类加载定义代理类

*@paraminterfacesthelistofinterfacesfortheproxyclass
*toimplement

    interfaces是代理类实现的interface的集合
*@paramhtheinvocationhandlertodispatchmethodinvocationsto

    h是一个handler用来分派方法去调用


@CallerSensitive
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.
*/
try{
if(sm!=null){
checkNewProxyPermission(Reflection.getCallerClass(),cl);
}

finalConstructor<?>cons=cl.getConstructor(constructorParams);
finalInvocationHandlerih=h;
if(!Modifier.isPublic(cl.getModifiers())){
AccessController.doPrivileged(newPrivilegedAction<Void>(){
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);
}
}


控制台输出结果

第一个结果com.sun.proxy.$Proxy0是System.out.println(subject.getClass().getName());

可以清晰地看到,我们用到了代理类,而不是自己定义的Subject类

com.sun.proxy.$Proxy0
beforerenthouse
Method:publicabstractvoidcom.advance.dynamic_proxy.Subject.rent()
Iwanttorentmyhouse
afterrenthouse
beforerenthouse
Method:publicabstractvoidcom.advance.dynamic_proxy.Subject.hello(java.lang.String)
hello:world
afterrenthouse


总结:

代理是通过代理类关联到InvocationHandler中的invoke方法调用了真实对象的方法,从而完成了代理过程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: