JDK动态代理

三世轮回 提交于 2020-03-07 08:00:03

设计模式之代理模式

通过增加代理来解耦A与C之间的调用,这样可以封装原来C调用A的一些相关细节,转换成C直接调用B中封装后的代理方法,则等同于访问A。对于WebService的远程调用时,如果我们使用添加Web引用的方式,那么WebService会为我们自动生成代理类的

所谓代理,就是代表某个真实对象,为另一个对象提供一个替身或占位符以控制对这个对象的访问。

 

JDK动态代理概念

运行时动态生成一个实际对象的代理,能过代理进行控制访问

与静态代理相比:

   动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展   性,因为Java 反射机制可以生成任意类型的动态代理类

JDK的动态代理是建立在接口编程上,如果一个类没有实现接口,则JDK无法生成相对应的代理类,并且只有接口中的方法能够被代理

应用:

  连接池的实现,调用close方法只是放回连接池而不是并闭,这样就可以使用代理代管close方法,如果方法名是close就使用自定义处理方法

  AOP的实现,在调用方法前后调用其他处理方法

JDK动态代理实现

  通过 java.lang.reflect.Proxy中的newProxyInstance方法创建一个代理类

InvocationHandler接口自定义处理逻辑

 

接口:

public interface PersonBean {

    String getName();

    void   setName(String name);

}

实现类:

public class PersonBeanImpl implements PersonBean {

    private String name;

   

    @Override

    public String getName() {

       // TODO Auto-generated method stub

       return this.name;

    }

 

    @Override

    public void setName(String name) {

       // TODO Auto-generated method stub

       this.name = name;

    }

}

 

自定义处理器:

public class PersonInvocationHandler implements InvocationHandler {

    private Object target;

   

    public PersonInvocationHandler(Object target){

       this.target = target;

    }

   

    @Override

    public Object invoke(Object proxy, Method method, Object[] args)

            throws Throwable {

       System.out.println("dynamic invoke begin...");

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

       //Object obj = method.invoke(proxy, args);    //传proxy这种写法有bug,java.lang.reflect.InvocationTargetException

       System.out.println("before dynamic end...");

       return obj;

    }

}

 

测试代码:

public class Test {

    /**

     * @param args

     */

    public static void main(String[] args) {

       // TODO Auto-generated method stub

       ProxyGeneratorUtils.writeProxyClassToHardDisk("d:/$Proxy11.class");

      

       PersonBean realPersonBean = new PersonBeanImpl();      

       PersonInvocationHandler personInvocationHandler = new PersonInvocationHandler(

              realPersonBean);

       //name=null 初始时为null

       System.out.println("name=" + realPersonBean.getName());

 

       PersonBean personBean = (PersonBean) Proxy.newProxyInstance(

               PersonBeanImpl.class.getClassLoader(), PersonBeanImpl.class

                       .getInterfaces(), personInvocationHandler);

       personBean.setName("aaa");

      

       //name=aaa   对代理对象的操作,实际是对目标对象的操作

       System.out.println("name=" + realPersonBean.getName());

    }

}

 

原理:

Proxy.newProxyInstance时,首先通过Proxy. getProxyClass()加载类,主要代码

       /*

        * Generate the specified proxy class.

        */

       byte[] proxyClassFile =  ProxyGenerator.generateProxyClass(

           proxyName, interfaces);

       try {

           proxyClass = defineClass0(loader, proxyName,

           proxyClassFile, 0, proxyClassFile.length);

       } catch (ClassFormatError e) {

           /*

            * A ClassFormatError here means that (barring bugs in the

            * proxy class generation code) there was some other

            * invalid aspect of the arguments supplied to the proxy

            * class creation (such as virtual machine limitations

            * exceeded).

            */

           throw new IllegalArgumentException(e.toString());

       }

生成二进制字节码,然后再加载到JVM中

 

 

加载后,调用代理类的构造函数生成实例对象     

 Constructor cons = cl.getConstructor(constructorParams);

return (Object) cons.newInstance(new Object[] { h });

 

具体动态生成的字节码可以

byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", PersonBeanImpl.class.getInterfaces());

输出到本地磁盘,反编译查看

 

  //代理类构造函数

  public Proxy11()

    throws

  {

    super(paramInvocationHandler);

  }

  //代理类中其中一个方法

  public final String getName()

    throws

  {

    try

    {

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

    }

    catch (RuntimeException localRuntimeException)

    {

      throw localRuntimeException;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

可以看到调用方法时,执行的是h.invoke(this, m3, null)

即自定义的InvocationHandler中的invoke方法

如果自定义中如下写:Object obj = method.invoke(proxy, args);

Proxy指向的是当前调用对象(代理类),则执行代理类中的method,

而代理类中的方法又是h.invoke(this, m3, null),这样一直递归调用下去

所以出现如下错误:

dynamic invoke begin...

dynamic invoke begin...

dynamic invoke begin...

dynamic invoke begin...

Exception in thread "main" java.lang.reflect.UndeclaredThrowableException

    at $Proxy0.setName(Unknown Source)

    at com.Test.main(Test.java:19)

Caused by: java.lang.reflect.InvocationTargetException

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    at com.PersonInvocationHandler.invoke(PersonInvocationHandler.java:18)

    ... 2 more

Caused by: java.lang.reflect.UndeclaredThrowableException

    at $Proxy0.setName(Unknown Source)

    ... 7 more

Caused by: java.lang.reflect.InvocationTargetException

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    at com.PersonInvocationHandler.invoke(PersonInvocationHandler.java:18)

    ... 8 more

Caused by: java.lang.reflect.UndeclaredThrowableException

    at $Proxy0.setName(Unknown Source)

    ... 13 more

 

处理方法,自定义处理器中的

public Object invoke(Object proxy, Method method, Object[] args)

            throws Throwable

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

 

Target指向目标对象,即被代理的对象,这样调用的是实际对象的方法了

 

使用代理对象操作实际目标对象,在代理对象上调用setName方法,实际会在目标对象上执行setName方法

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!