zoukankan      html  css  js  c++  java
  • Java的动态代理

     转载请注明出处:https://www.cnblogs.com/zhizaixingzou/p/10079404.html

    目录

    1. 动态代理

    1.1. 设计模式之代理模式

    转自:https://www.dofactory.com/net/design-patterns

    1)代理模式的定义:

    Provide a surrogate or placeholder for another object to control access to it.

    2)代码模式各个角色:

    Proxy   (MathProxy)

    maintains a reference that lets the proxy access the real subject. Proxy may refer to a Subject if the RealSubject and Subject interfaces are the same. 代理也可能不保存RealSubject,如Java RMI动态代理中,代理并不直接指向远程对象即RealSubject,而是与远程对象通信的一个类)。

    provides an interface identical to Subject's so that a proxy can be substituted for for the real subject.

    controls access to the real subject and may be responsible for creating and deleting it.

    other responsibilites depend on the kind of proxy:

    1.remote proxies are responsible for encoding a request and its arguments and for sending the encoded request to the real subject in a different address space.

    2.virtual proxies may cache additional information about the real subject so that they can postpone accessing it. For example, the ImageProxy from the Motivation caches the real images's extent.

    3.protection proxies check that the caller has the access permissions required to perform a request.

    Subject   (IMath)

    defines the common interface for RealSubject and Proxy so that a Proxy can be used anywhere a RealSubject is expected. 

    RealSubject   (Math)

    defines the real object that the proxy represents.

    3结构代码(静态代理C#源码

    ·  using System;

    ·   

    ·  namespace DoFactory.GangOfFour.Proxy.Structural

    ·  {

    ·    class MainApp

    ·    {

    ·      static void Main()

    ·      {

    ·        Proxy proxy = new Proxy();

    ·        proxy.Request();

    ·        Console.ReadKey();

    ·      }

    ·    }

    ·   

    ·    abstract class Subject

    ·    {

    ·      public abstract void Request();

    ·    }

    ·   

    ·    class RealSubject : Subject

    ·    {

    ·      public override void Request()

    ·      {

    ·        Console.WriteLine("Called RealSubject.Request()");

    ·      }

    ·    }

    ·   

    ·    class Proxy : Subject

    ·    {

    ·      private RealSubject _realSubject;

    ·   

    ·      public override void Request()

    ·      {

    ·        if (_realSubject == null)

    ·        {

    ·          _realSubject = new RealSubject();

    ·        }

    ·   

    Console.WriteLine("before");

    ·        _realSubject.Request();

    Console.WriteLine("after");

    ·      }

    ·    }

    ·  }

    1.2. 两种代理模式

    代理模式是常用的设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等按照代理的创建时期,代理类可以分为两种。 

    1静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了 

    2动态代理:在程序运行时,运用反射机制动态创建而成

    1.3. 动态代理编程实例

    主题类及其代理的共有接口:

    package com.cjw.learning.proxy;

    public interface Person {
        void say();
    }

    主题类实现:

    package com.cjw.learning.proxy;

    public class PersonImpl implements Person {
        @Override
        public void say() {
            System.out.println("I am Mr. Bit.");
        }
    }

    代理实现:

    package com.cjw.learning.proxy;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    public class PersonHandler implements InvocationHandler {
        private Object obj;

        private PersonHandler(Object obj) {
            this.obj = obj;
        }

        public static Object getProxy(Object obj) {
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new PersonHandler(obj));
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            Object result = method.invoke(obj, args);
            System.out.println("after");
            return result;
        }
    }

    客户端程序:

    package com.cjw.learning.proxy;

    public class Demo01 {
        public static void main(String[] args) {
            Person person = new PersonImpl();
            Person proxy = (Person) PersonHandler.getProxy(person);
            proxy.say();
        }
    }

    程序输出:

    1.4. 动态代理实现原理基于JDK的动态代理

    上面的动态代理是基于JDK的反射实现的,接下来详细了解其过程。

    1)首先,创建一个主题类实例。

    Person person = new PersonImpl();

    2)接下来,根据公共接口动态生成一个代理类$Proxy0字节码文件并加载,并使用它的带InvocationHandler的构造方法创建该代理类的实例

    Person proxy = (Person) PersonHandler.getProxy(person);

    public static Object getProxy(Object obj) {
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new PersonHandler(obj));
    }

    3)创建代理类字节码文件的方法如下:

    可以看到,这里是根据动态生成的代理类的名称、被代理类实现的接口的信息和类标志来生成字节码然后用本地方法生成得到Class的实例。

    4为了更进一步研究,使用同样的方法将生成的字节码写到磁盘

    public static void main(String[] args) {
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", PersonImpl.class.getInterfaces(), 17);

        FileOutputStream out = null;
        try {
            out = new FileOutputStream("C:\Users\phewy\Desktop\$Proxy0.class");
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    反编译查看生成的字节码:

    import com.cjw.learning.proxy.Person;

    import java.lang.reflect.InvocationHandler;

    import java.lang.reflect.Method;

    import java.lang.reflect.Proxy;

    import java.lang.reflect.UndeclaredThrowableException;

    public final class $Proxy0

      extends Proxy

      implements Person

    {

      private static Method m1;

      private static Method m2;

      private static Method m3;

      private static Method m0;

      

      public $Proxy0(InvocationHandler paramInvocationHandler)

      {

        super(paramInvocationHandler);

      }

      

      public final boolean equals(Object paramObject)

      {

        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 String toString()

      {

        try

        {

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

        }

        catch (Error|RuntimeException localError)

        {

          throw localError;

        }

        catch (Throwable localThrowable)

        {

          throw new UndeclaredThrowableException(localThrowable);

        }

      }

      

      public final void say()

      {

        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()

      {

        try

        {

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

        }

        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") });

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

          m3 = Class.forName("com.cjw.learning.proxy.Person").getMethod("say", new Class[0]);

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

          return;

        }

        catch (NoSuchMethodException localNoSuchMethodException)

        {

          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());

        }

        catch (ClassNotFoundException localClassNotFoundException)

        {

          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());

        }

      }

    }

    this表示生成的代理类的对象本身,而h是对应的InvocationHandler类的对象(实现类就封装在里面)。可以看到,此法是基于接口来实现代理的。

    1.5. 基于cglib的动态代理

    JDK动态代理的前提,是目标类基于统一的接口cglib是一个优秀的动态代理框架,它的底层使用ASM内存中动态的生成被代理类的子类,使用cglib即使代理类没有实现任何接口也可以实现动态代理功能cglib具有简单易用,它的运行速度要远远快于JDKProxy动态代理

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.9</version>
    </dependency>

    package com.cjw.learning.proxy;

    public class Hello {
        public String say() {
            return "Hello";
        }
    }

    package com.cjw.learning.proxy;

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;

    import java.lang.reflect.Method;

    public class Demo02 {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Hello.class);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invokeSuper(obj, params) + " in " + method.getName();
                }
            });

            Hello hello = (Hello) enhancer.create();
            System.out.println(hello.say());
        }
    }

    关于它的原理,在相应的框架章节中讲解。

    1.6. 参考资料

    JDK源码

    https://www.dofactory.com/net/design-patterns

    https://github.com/cglib/cglib/tree/RELEASE_3_2_9

  • 相关阅读:
    js实现各种复制到剪贴板的方法
    PowerDesigner生成数据字典
    oracle实用sql之将逗号分割的字符串分割多个列
    ROW_NUMBER() OVER()函数用法;(分组,排序),partition by
    SQL Server 2008中的CTE递归查询得到一棵树
    【GoLang】GoLang 错误处理 -- 使用 error is value 的思路处理,检查并处理error
    【GoLang】golang 报管理工具 Godep 介绍
    【GoLang】GoLang 错误处理 -- 使用异常的思路进行处理
    【GoLang】GoLang 官方 对 error 处理的意见
    【GoLang】panic defer recover 深入理解
  • 原文地址:https://www.cnblogs.com/zhizaixingzou/p/10079404.html
Copyright © 2011-2022 走看看