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

Java动态代理原理

2015-12-10 23:29 274 查看
JAVA动态代理原理

概述

同为代理,动态代理和静态代理本质上其实没什么区别,代理对象都有被代理对象的引用,并通过调用被代理对象的业务逻辑(方法),实现代理操作。对于一般程序员来说,动态代理好像要比静态代理难理解且实现起来更麻烦一些。本文档解释了动态代理的本质,通过文档的学习,你会发现动态代理也是非常简单易用的。

动态代理结构

为了方面描述,这里先假设一个动态代理的场景,并简单实现它。这个场景的原型是一个具体的项目,项目中有很多的服务类,我们用动态代理的目的是为了在每一个服务对象的方法被调用的时候做一些预处理,其中一项就是打印日志。我们可以在每个服务类方法中添加这样的业务逻辑,但方法确实太多,一个个添加会有很多多余代码。而且预处理的逻辑可能会改变,那么我们就需要更改所有的服务类了,这是一个非常不容易完成的任务。所以我们使用了动态代理。

为了方便,这里已经简化了服务类。假设某个服务类有接口IServer:

package com.test.proxy.impl;

public interface IServer {

publicvoid start();

publicvoid process();

publicvoid stop();

}

具体的服务类:

package com.test.proxy.impl;

public class Serverimplements IServer{

@Override

publicvoid
start() {

System.out.println("start");

}

@Override

publicvoid process() {

System.out.println("process");

}

@Override

publicvoid stop() {

System.out.println("stop");

}

}

我们首先实现一个DefaultHandler:

package com.test.proxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

public class DefaultHandlerimplements InvocationHandler{

private Objectobj;

public DefaultHandler(Objectobj){

this.obj =obj;

}

@Override

public Object invoke(Objectobject, Method
method, Object[] args)
throws Throwable {

/**
方法调用前预处理 **/

System.out.println("method:"+method.getName()+" start");

Object ret =method.invoke(obj,args);

/**
方法调用后处理 **/

System.out.println("method:"+method.getName()+" end");

returnret;

}

}

这个类实现了InvocationHandler接口,并实现了invoke方法,代理类的所有方法调用都会调用invoke方法。

最后我们通过一个带有main入口的类,来实现这个代理。

package com.test.proxy;

import java.lang.reflect.Proxy;

import com.test.proxy.impl.IServer;

import com.test.proxy.impl.Server;

public class BusinessMain {

publicstatic
void main(String[]args) {

//初始化一个被代理对象的实例

Server serv =new Server();

//初始化handler,并将被代理对象的引用传递给它

DefaultHandler defaultHandler =new DefaultHandler(serv);

//获得代理对象实例

IServer proxyServ = (IServer)Proxy.newProxyInstance(serv.getClass().getClassLoader(),serv.getClass().getInterfaces(),defaultHandler);

//通过代理对象调用方法

proxyServ.start();

proxyServ.stop();

}

}

运行main函数,就能得到以下输出

method:start start

start

method:start end

method:stop start

stop

method:stop end

好了一个完整的动态代理已经完成了,现在我们分析一下,这是怎么实现的。

1.和静态代理一样,我们首先构造了一个被代理对象的实例。

2.构造DefaultHandler实例,并把被代理对象实例的引用传递给了这个实例,这时候DefaultHanlder的实例就包含了被代理对象的引用了,这里和静态代理有点相似了,静态代理类里也包含了被代理对象的引用。

3.构造代理对象实例,静态代理时,我们会自己完成代理类的编写。动态代理中我们通过Proxy类的静态方法newProxyInstance就获得了代理对象,并没有编写代理类。实际上我们调用的这个静态方法,直接动态生成了代理类的字节码,并通过我们传递的类加载器加载了它,然后初始化生成了代理对象。

我们看一下静态方法的代码:

    public static Object newProxyInstance(ClassLoaderloader,

  Class<?>[] interfaces,

  InvocationHandler
h)

throws IllegalArgumentException

    {

if (h ==null) {

    thrownew NullPointerException();

}

/*

* Look up or generate the designated proxy class.

*/

        Class<?> cl = getProxyClass0(loader, interfaces);// stack walk magic: do not refactor

/*

* Invoke its constructor with the designated invocation handler.

*/

try {

            final Constructor<?> cons = cl.getConstructor(constructorParams);

            final InvocationHandlerih =
h;

            SecurityManager sm = System.getSecurityManager();

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

                // create proxy instance with doPrivilege as the proxy class may

                // implement non-public interfaces that requires a special permission

                return AccessController.doPrivileged(new PrivilegedAction<Object>() {

                    public Object run() {

                        returnnewInstance(cons, ih);

                    }

                });

            } else {

                return newInstance(cons,ih);

            }

} catch (NoSuchMethodExceptione) {

    thrownew InternalError(e.toString());



    }

这段代码首先通过我们提供的类加载器和接口,动态生成了代理类字节码,并加载获得了Class对象。

Class<?> cl = getProxyClass0(loader, interfaces);

通过Class对象得到构造器

final Constructor<?> cons = cl.getConstructor(constructorParams);

最后用我们提供的Handler实例作为参数,构造了一个代理类对象。代理类对象中包含了对handler实例的引用。

现在我们具体就来谈谈代理类对象。

首先代理类对象是实现了IServer接口的,那么它就包含所有的IServer声明的方法,那么它到底是如何调用Handler中Invoke方法的呢。其实我们看看生成的代理类,就一目了然了。

import com.test.proxy.impl.IServer;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.lang.reflect.UndeclaredThrowableException;

public final class ProxyCodes extends Proxy

  implements IServer

{

  private static Method m1;

  private static Method m5;

  private static Method m4;

  private static Method m3;

  private static Method m0;

  private static Method m2;

  public ProxyCodes(InvocationHandler paramInvocationHandler)

    throws 

  {

    super(paramInvocationHandler);

  }

  public final boolean equals(Object paramObject)

    throws 

  {

    try

    {

      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  public final void process()

    throws 

  {

    try

    {

      this.h.invoke(this, m5, null);

      return;

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  public final void stop()

    throws 

  {

    try

    {

      this.h.invoke(this, m4, null);

      return;

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  public final void start()

throws 

  {

    try

    {

      this.h.invoke(this, m3, null);

      return;

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  public final int hashCode()

    throws 

  {

    try

    {

      return ((Integer)this.h.invoke(this, m0, null)).intValue();

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  public final String toString()

    throws 

  {

    try

    {

      return (String)this.h.invoke(this, m2, null);

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  static

  {

    try

    {

      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });

      m5 = Class.forName("com.test.proxy.impl.IServer").getMethod("process", new Class[0]);

      m4 = Class.forName("com.test.proxy.impl.IServer").getMethod("stop", new Class[0]);

      m3 = Class.forName("com.test.proxy.impl.IServer").getMethod("start", new Class[0]);

      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

      return;

    }

    catch (NoSuchMethodException localNoSuchMethodException)

    {

      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());

    }

    catch (ClassNotFoundException localClassNotFoundException)

    {

      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());

    }

  }

}

代理类声明并获得了所有接口和Object类的所有方法对象的引用。代理类在调用方法时,调用Handler的invoke方法并传递参数,参看invoke方法的实现,我们就能知道整个动态代理的逻辑了。

补充说明

封装代理获得方法

关于动态代理的实现我们一般会把初始化Handler和获得代理类实例的代码封装起来,以便在使用代理的时候能和直接new一个对象一样方便。下面是一个简单的例子,只是提供一种思路。

我们实现一个DynamicProxy类来封装代码。

package com.test.proxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Proxy;

public class DynamicProxy {

/**

* get
proxy with object and handler

* @param object

* @param handler

* @return

*/

publicstatic Object getProxy(Object
object,InvocationHandler handler){

//TODO

returnnull;

}

/**

* get
proxy with object

* @param object

* @return Object

*/

publicstatic Object getProxy(Object
object){

DefaultHandler defaultHandler =new DefaultHandler(object);

return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),defaultHandler);

}

/**

* get
proxy with class and handler

* @param clazz

* @param handler

* @return

*/

publicstatic Object getProxy(Class<?>
clazz,InvocationHandler handler){

//TODO

returnnull;

}

/**

* get
proxy with class

* @param clazz

* @return Object

*/

publicstatic Object getProxy(Class<?>
clazz){

//TODO

returnnull;

}

}

在实际调用中,就可以这样完成:

package com.test.proxy;

import com.test.proxy.impl.IServer;

import com.test.proxy.impl.Server;

public class ProxyMain {

publicstatic
void main(String[]args) {

Server serv =new Server();

IServer proxyServ = (IServer)DynamicProxy.getProxy(serv);

//method invoke

proxyServ.start();

proxyServ.stop();

}

}

比起第一个显然要简单了一些,我们还可以进一步封装,使得使用动态代理变得更灵活和方便。

动态代理类字节码生成

JDK1.6中看Proxy方法中的源码,可以看到字节码生成代码是通过ProxyGenerator类的方法实现的。

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces);

本文档字节码生成代码如下:

package com.test.proxy;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import sun.misc.ProxyGenerator;

import com.test.proxy.impl.Server;

public class GenrateBinaryCodes {

publicstatic
void main(Stringargs[])
throws IOException{

//Generate the code for ProxyClass

byte[]classCode = ProxyGenerator.generateProxyClass("ProxyCodes", Server.class.getInterfaces());

//Persist to file

FileOutputStream
fos = new FileOutputStream(new File("ProxyCodes.class"));

fos.write(classCode);

fos.flush();

fos.close();

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: