代理模式(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

原文地址:https://www.cnblogs.com/UpGx/p/14690969.html