设计模式-代理模式

目录

1. 概述

1.1 定义

1.2 作用

2. 实现代理的方式

2.1 静态代理

2.1.1 特点

2.1.2 代码举例

2.1.3 优点和缺点

2.2 动态代理

2.2.1 特点

2.2.2 分类

2.2.3 应用场景

2.2.4 代码举例

2.2.5 优点

3. 原理分析

4. 结论

5. 参考文档


1. 概述

1.1 定义

为其他对象提供一种代理从而实现对这个对象的访问,在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成

1.2 作用

  • 功能增强:使用代理对象,可以在不修改被代理对象的逻辑的基础上,在前面或者后面增加其他业务逻辑,实现功能增强

  • 控制访问:一个对象不能直接引用另一个对象,通过代理模式间接引用,起到类似于中介的作用

2. 实现代理的方式

2.1 静态代理

2.1.1 特点

  • Java中的静态代理要求代理类(ProxySubject)和委托类(RealSubject)都实现同一个接口

  • 静态代理能够在编译时期确定类的执行对象,而动态代理只有在运行时才能够确定执行对象是谁,因为通过反射生成对应的实例

2.1.2 代码举例

被代理接口

//抽象角色:租房
public interface Rent {
   public void rent();
}

被代理类

//真实角色: 房东,房东要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}

代理类

//代理角色:中介
public class Proxy implements Rent {

   private Host host;
   public Proxy() { }
   public Proxy(Host host) {
       this.host = host;
  }

   //租房
   public void rent(){
       seeHouse();
       host.rent();
       fare();
  }
   //看房
   public void seeHouse(){
       System.out.println("带房客看房");
  }
   //收中介费
   public void fare(){
       System.out.println("收中介费");
  }
}

代码运行

//客户类,一般客户都会去找代理!
public class Client {
   public static void main(String[] args) {
       //房东要租房
       Host host = new Host();
       //中介帮助房东
       Proxy proxy = new Proxy(host);
       //你去找中介!
       proxy.rent();
  }
}

2.1.3 优点和缺点

  • 业务代码更加集中;业务和公共代码区分开来,耦合性更高,提高开发效率;业务发生改变扩展方便,静态代理相对于动态代理效率要高

  • 客户类/被代理类增多,代理类也跟着变多,如果修改代码所有类都要跟着变动

2.2 动态代理

2.2.1 特点

我们上面静态代理的例子中,代理类(studentProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法,比如说,想要在每个代理的方法前都加上一个处理方法

    public void giveMoney() {
        //调用被代理方法前加入处理方法
        beforeMethod();
        stu.giveMoney();
    }

2.2.2 分类

一类是基于接口动态代理 , 一类是基于类的动态代理

  • 基于接口的动态代理----JDK动态代理

  • 基于类的动态代理–cglib

2.2.3 应用场景

如果很多方法前面或者后面都有相同的方法要去执行,那你每次在方法执行前都要对相同的代码重新书写一遍,这个时候建议使用动态代理去实现

  • 权限 集中 申请
  • 日志集中打印
  • 全局性异常处理、事务处理

2.2.4 代码举例

1.确定创建接口具体行为

首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上交班费的行为。这样,学生上交班费就可以让班长来代理执行

2.定义接口

/**
 * 创建Person接口
 */
public interface Person {
    //上交班费
    void giveMoney();
}

3.被代理对象实现接口,完成具体的业务逻辑(此处增加一些方法用于检测后面使用动态代理用于区分)

public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    
    @Override
    public void giveMoney() {
        try {
            //假设数钱花了一秒时间
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       System.out.println(name + "上交班费50元");
    }
}

4.代理类实现接口,完成委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等

  • InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法
  • invoke方法中执行被代理对象target的相应方法。在代理过程中,我们在真正执行被代理对象的方法前加入自己其他处理。这也是Spring中的AOP实现的主要原理
  • 这里还涉及到一个很重要的关于java反射方面的基础知识
public class StuInvocationHandler implements InvocationHandler {
    //invocationHandler持有的被代理对象
    T target;
    
    public StuInvocationHandler(T target) {
       this.target = target;
    }
    
    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" +method.getName() + "方法");  
        //代理过程中插入监测方法,计算该方法耗时
        MonitorUtil.start();
        Object result = method.invoke(target, args);
        MonitorUtil.finish(method.getName());
        return result;
    }
}

5.客户端使用操作与分析

public class ProxyTest {
    public static void main(String[] args) {
        
        //创建一个实例对象,这个对象是被代理的对象
        Person zhangsan = new Student("张三");
        
        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler(zhangsan);
        
        //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, stuHandler);
 
       //代理执行上交班费的方法
        stuProxy.giveMoney();
    }
}

6. 计时工具类

public class MonitorUtil {
    
    private static ThreadLocal tl = new ThreadLocal<>();
    
    public static void start() {
        tl.set(System.currentTimeMillis());
    }
    
    //结束时打印耗时
    public static void finish(String methodName) {
        long finishTime = System.currentTimeMillis();
        System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
    }
}

所有的被代理对象执行的方法都会被计时,然而我只做了很少的代码量

2.2.5 优点

  • 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法

  • 动态代理非常的灵活,可以为任意的接口实现类对象做代理

  • 代理类在JVM运行时动态生成,而不是编译期就能确定

  • 提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类

  • 动态代理中被代理对象和代理对象是通过InvocationHandler来完成的代理过程的

3. 原理分析

看一下Proxy类的newProxyInstance静态方法

@CallerSensitive 
public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
		//检查h 不为空,否则抛异常
        Objects.requireNonNull(h);
 
        final Class[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
 
        /*
         * 获得与指定类装载器和一组接口相关的代理类类型对象
         */
        Class cl = getProxyClass0(loader, intfs);
 
        /*
         * 通过反射获取构造函数对象并生成代理类实例
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
			//获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h)) 
            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
			//生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

Class cl = getProxyClass0(loader, intfs);这里面获取代理类,通过加载器和代理类类型

 private static Class getProxyClass0(ClassLoader loader,
                                           Class... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
 
        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

 通过缓存获取代理类

/**
* a cache of proxy classes
*/
private static final WeakCache[], Class>
  proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

那么它对应的get方法啥样呢?

public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);
 
        expungeStaleEntries();
 
        Object cacheKey = CacheKey.valueOf(key, refQueue);
 
        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
			//putIfAbsent这个方法在key不存在的时候加入一个值,如果key存在就不放入
            ConcurrentMap> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }
 
        // create subKey and retrieve the possible Supplier stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier supplier = valuesMap.get(subKey);
        Factory factory = null;
 
        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)
 
            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
 
            if (supplier == null) {				
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

 我们可以看到它调用了 supplier.get(); 获取动态代理类,其中supplier是Factory,这个类定义在WeakCach的内部。来瞅瞅,get里面又做了什么?

 public synchronized V get() { // serialize access
            // re-check
            Supplier supplier = valuesMap.get(subKey);
            if (supplier != this) {
                // something changed while we were waiting:
                // might be that we were replaced by a CacheValue
                // or were removed because of failure ->
                // return null to signal WeakCache.get() to retry
                // the loop
                return null;
            }
            // else still us (supplier == this)
 
            // create new value
            V value = null;
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // the only path to reach here is with non-null value
            assert value != null;
 
            // wrap value with CacheValue (WeakReference)
            CacheValue cacheValue = new CacheValue<>(value);
 
            // try replacing us with CacheValue (this should always succeed)
            if (valuesMap.replace(subKey, this, cacheValue)) {
                // put also in reverseMap
                reverseMap.put(cacheValue, Boolean.TRUE);
            } else {
                throw new AssertionError("Should not reach here");
            }
 
            // successfully replaced us with new CacheValue -> return the value
            // wrapped by it
            return value;
        }
    }

我们可以看到它调用了valueFactory.apply(key, parameter)方法

 /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction[], Class>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";
 
        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
 
        @Override
        public Class apply(ClassLoader loader, Class[] interfaces) {
 
            Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
 
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
 
            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
 
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
 
            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
 
            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return 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());
            }
        }
    }

 通过看代码终于找到了重点:

//生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

那么接下来我们也使用测试一下,使用这个方法生成的字节码是个什么样子:

package jiankunking;
 
import sun.misc.ProxyGenerator;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
 
/**
 * 动态代理演示
 */
public class DynamicProxyDemonstration
{
    public static void main(String[] args)
    {
        //代理的真实对象
        Subject realSubject = new RealSubject();
 
        /**
         * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
         * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
         * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
         */
        InvocationHandler handler = new InvocationHandlerImpl(realSubject);
 
        ClassLoader loader = handler.getClass().getClassLoader();
        Class[] interfaces = realSubject.getClass().getInterfaces();
        /**
         * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
         */
        Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
 
        System.out.println("动态代理对象的类型:"+subject.getClass().getName());
 
        String hello = subject.SayHello("jiankunking");
        System.out.println(hello);
        // 将生成的字节码保存到本地,
        createProxyClassFile();
    }
    private static void createProxyClassFile(){
        String name = "ProxySubject";
        byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class});
        FileOutputStream out =null;
        try {
            out = new FileOutputStream(name+".class");
            System.out.println((new File("hello")).getAbsolutePath());
            out.write(data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null!=out) try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
}

 可以看一下这里代理对象的类型:com.sun.proxy.$proxy0

 我们用jd-jui 工具将生成的字节码反编译:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import jiankunking.Subject;
 
public final class ProxySubject
  extends Proxy
  implements Subject
{
  private static Method m1;
  private static Method m3;
  private static Method m4;
  private static Method m2;
  private static Method m0;
  
  public ProxySubject(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 SayGoodBye()
  {
    try
    {
      return (String)this.h.invoke(this, m3, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String SayHello(String paramString)
  {
    try
    {
      return (String)this.h.invoke(this, m4, new Object[] { paramString });
    }
    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 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") });
      m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
      m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
      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());
    }
  }
}

public final class ProxySubject extends Proxy implements Subject,这就是最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,也就是说:

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

这里的subject实际是这个类的一个实例,那么我们调用它的:

public final String SayHello(String paramString)

就是调用我们定义的InvocationHandlerImpl的 invoke方法:

上面代码中的这个位置

public final String SayHello(String paramString)
  {
    try
    {
      return (String)this.h.invoke(this, m4, new Object[] { paramString });
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

4. 结论

这就解释了为什么调用代理类方法的时候,调用的是接口方法,实际上能够自动调用InvocationHandler实现类的invoke方法

因为jdk生成的最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,在实现Subject接口方法的内部,通过反射调用了InvocationHandlerImpl的invoke方法。

具体实现步骤:

  • 通过实现 InvocationHandler 接口创建自己的调用处理器;
  • 通过为 Proxy 类指定 ClassLoader 对象和一组interface来创建动态代理
/*
         * Look up or generate the designated proxy class.
         */
        Class cl = getProxyClass0(loader, intfs);
  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入;
final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});

 简而言之,最前面的分析就是生成代理类,后面通过构造函数把调用处理器进行赋值,调用里面的invoke方法

public final String SayGoodBye()
  {
    try
    {
      return (String)this.h.invoke(this, m3, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

5. 参考文档

Java动态代理的使用及原理分析 - JAVA编程语言程序开发技术文章 - 红黑联盟

详解动态代理及其实现原理_二缺和傻宝宝的博客-CSDN博客_动态代理的实现原理

你可能感兴趣的:(设计模式,代理模式,设计模式)