• 代理模式(Proxy Pattern)


    代理模式(Proxy Pattern)

    对其他对象提供代理,以控制对这个对象的访问,目的对其他对象进行增强,简而言之,类似AOP,他就是一种代理模式的实现,对你的类进行增强(在你的方法或者类调用之前、后进行增强),代理模式分为两种,静态和动态代理,动态代理常常有两种类库用来实现(CGLIB和JDK).以下先从应用入手,然后阐述原理。

    staticproxy

    public class Glen implements IPerson {
        public void findRealPeople() {
            System.out.println("I want a well-educated、sexy girl");
        }
    }
    public interface IPerson {
        void findRealPeople();
    }
    public class GlenFather implements IPerson {
    
        Glen glen;
    
        GlenFather (Glen glen){
            this.glen=glen;
        }
    
        public void findRealPeople() {
            before();
            glen.findRealPeople();
            after();
        }
    
        // strengthen
        private void after() {
            System.out.println("begin to find a real girl for glen");
        }
    
        // strengthen
        private void before() {
            System.out.println("find a rich girl for glen and sexy and love my son");
        }
    
    }

    To text

    public class Test {
        public static void main(String[] args) {
            GlenFather father=new GlenFather(new Glen());
            father.findRealPeople();
        }
    }

    To explain

     The image above illustrate that before 'Glen#findRealPeople' was implemented ,the 'GlenFather#before'  was implemented which  strengthened method of Glen ,and 'GlenFather#after' was implemented which also strengthened   method of Glen! but the problem is that if some others want to  findRealPeople,do they need to created a new classes to strengthen their method?no! it is bit foolish,now we to solve the problem with  DynamicProxy

    Dynamic Proxy

    JdkProxy

    public class Glen implements IPerson {
        public void findRealPeople() {
            System.out.println("Glen:I want a well-educated、sexy girl");
        }
    }
    public interface IPerson {
        void findRealPeople();
    }
    public class JdkProxy implements InvocationHandler {
        IPerson iPerson;
        public IPerson getInstance(IPerson iPerson){
            this.iPerson=iPerson;
            Class<? extends IPerson> aClass = iPerson.getClass();
            // here to use Proxy create
            return (IPerson) Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
        }
    
        /**
         *
         * @param proxy the classes generated
         * @param method the method that you want to use
         * @param args the params  that you pass on the method
         * @return
         * @throws Throwable
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // have a look here,if you just want to implement certain method you can use name of method to control
            System.out.println(method.getName());
    
            before();
    
            Object invoke = method.invoke(this.iPerson, args);
            after();
            return invoke;
        }
    
        // strengthen
        private void before() {
            System.out.println("find a rich girl for someone else and sexy and love my customers");
        }
        // strengthen
        private void after() {
            System.out.println("begin to find a real girl for someone else");
        }
    
    }
    public class Justine implements IPerson {
        public void findRealPeople() {
            System.out.println("Justine:the girl should  taller than me");
        }
    }

    To test

    public class Test {
        public static void main(String[] args) {
            JdkProxy jdkProxy=new JdkProxy();
            // the instance that is generated by jdkProxy
            IPerson glenInstance= jdkProxy.getInstance(new Glen());
            glenInstance.findRealPeople();
            System.out.println("---------------------------------->");
            IPerson justineInstance = jdkProxy.getInstance(new Justine());
            justineInstance.findRealPeople();
        }
    }

    To explain

     

     We can proxy the classes that implements the same interface with jdkproxy in 'java.lang.reflect'

    CGLIB

    public class CglibProxy implements MethodInterceptor {
        public Object getInstance(Class clazz) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
    
        // strengthen
        private void before() {
            System.out.println("find a rich girl for someone else and sexy and love my customers");
        }
        // strengthen
        private void after() {
            System.out.println("begin to find a real girl for someone else");
        }
    
        /**
         *
         * @param o the classes you pass on
         * @param method  the methods you execute
         * @param objects the params you pass on the methods you execute
         * @param methodProxy method of  proxy
         * @return
         * @throws Throwable
         */
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            before();
            Object result = methodProxy.invokeSuper(o, objects);
            after();
            return result;
        }
    }
    public class Glen  {
        public void findRealPeople() {
            System.out.println("Glen:I want a well-educated、sexy girl");
        }
    }

    To test

    public class Test {
        public static void main(String[] args) {
            CglibProxy cglibProxy=new CglibProxy();
            Glen glen= (Glen) cglibProxy.getInstance(Glen.class);
            glen.findRealPeople();
        }
    }

    To explain

     you can find out the different points

    • The class of Glen do not has to implements any interfaces in cglib but has to do in jdk
    • We should gave class of Glen to cglib but we should give Glen.interfaces to jdk

    so in base jdkproxy and cglib have different method to carry out,

    • for cglib  it extends  the class you want to be proxy,
    • for jdkproxy it implements class that you want to be proxy,
    • that is the reason why you should pass the class to cglib  but interfaces to  jdkproxy 

    Theory of  Dynamic proxy 

    Theory of JDK

     so i thought whether we can get bytecode of the proxy of class and put it into disk ,then compile the file with '.class' Run!!! 

    public class Test {
        public static void main(String[] args) throws Exception {
            JdkProxy jdkProxy=new JdkProxy();
            // the instance that is generated by jdkProxy
            IPerson glenInstance= jdkProxy.getInstance(new Glen());
            glenInstance.findRealPeople();
    
    
            // we can get  bytecode with  ProxyGenerator
            byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IPerson.class});
            FileOutputStream fileOutputStream=new FileOutputStream("C:\Users\lizi\Desktop\file\$Proxy0.class");
            fileOutputStream.write($Proxy0s);
            fileOutputStream.close();
    
        }
    }

    This is decompiled classes

    import com.growup.design.proxy.dynamicproxy.jdkproxy.IPerson;
    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 IPerson
    {
      private static Method m1;
      private static Method m3;
      private static Method m2;
      private static Method m0;
    
      public $Proxy0(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 (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final void findRealPeople()
        throws 
      {
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final String toString()
        throws 
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final int hashCode()
        throws 
      {
        try
        {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("com.growup.design.proxy.dynamicproxy.jdkproxy.IPerson").getMethod("findRealPeople", new Class[0]);
          m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
      }
    }

    To explain

    • we implement IPerson so that we have methods of  IPerson 
    • the method named findRealPeople execute h.invoke, h is param of 'InvocationHandler h 'in 'java.lang.reflect.Proxy#newProxyInstance' ,we have pass on the JDKProxy.then method named invoke in JDKProxy

    we find out  m3  has assigned

     in short,when we execute method actually execute method named 'JdkProxy#invoke'

    CGLIB of JDK

    public class Test   {
        public static void main(String[] args) {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"C:\Users\lizi\Desktop\file\cglib_proxy_classes");
            CglibProxy cglibProxy=new CglibProxy();
            Glen glen= (Glen) cglibProxy.getInstance(Glen.class);
            glen.findRealPeople();
        }
    }

    we can find classes decompiled, but them are very long therefor,i do not want to put the codes here,just explain

     

     we open the file find out it extends the Glen and  rewrite the method named findRealPeople

     

     SUM UP

    advantages:

    • To seperate the classes of proxy and real classes,in some extent which reduce the coupling of the system
    • To strengthen object classes

    disadvantages:

    • improve the complexity of system
    • may diminish the efficieny of system

    Be use in practic:

      such as  change  data  source by conditions, in each methods of inserting shoud to change data source according to the time or something else, we can extract common logical code in classes of proxy and just do certain logic code in each classes

    tips:

    • in framwork of Spring, if your beans did not implement any interfaces than it tend use the CGLIB otherwise use JDK,We can of course make changes through configuration, that is 'proxy-target-class' we can set it to true than framework of spring will default use the CGLIB
    • CGLIB more efficient than JDKPROXY because  CGLIB don't use reflect in base

  • 相关阅读:
    Linux常用命令琐记
    JDK 在linux下支持epoll了
    八卦
    JDK 1.6中的并发
    关于Atomic
    关于并发程序设计(二)
    关于并发程序设计 (一)
    Herb Sutter的一些观点
    想到Exchanger N parties的一种用法
    该拒绝MSN Messager了
  • 原文地址:https://www.cnblogs.com/UpGx/p/14690969.html
Copyright © 2020-2023  润新知