设计代理的实际目的是什么class?

What is the actual purpose of designing a proxy class?

我一直在研究代理 classes,但我不了解设计它的全部思路。

据我目前所知,它是一个包装对象,可以控制对原始对象的访问。但是如果我们想控制它,为什么我们不能用那些访问机制设计原始的class。

我读到这些代理对象对于跟踪方法调用、将方法调用路由到远程服务器很有用。

但是我在 java 中搜索了一个可以向我解释这个问题的问题,但我没有找到。

我将说明我所指的书中的方法跟踪程序的代码。

public class ProxyTest {

  public static void main(String[] args) throws ClassNotFoundException {

     var elements = new Object[1000];

     // fill elements with proxies for the integers 1 . . . 1000
     for (int i = 0; i < elements.length; i++) {
       Integer value = i + 1;
       var handler = new TraceHandler(value);
       Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Comparable.class}, handler);
       elements[i] = proxy;
     }

     // construct a random integer
     Integer key = new Random().nextInt(elements.length) + 1;

     // search for the key
     int result = Arrays.binarySearch(elements, key);

     // print match if found
     if (result >= 0)
        System.out.println(elements[result]);

  }

}

/**
 * An invocation handler that prints out the method name and parameters, then
 * invokes the original method
 **/

class TraceHandler implements InvocationHandler{

  private Object target;

  /**
   * Constructs a TraceHandler
   * @param t the implicit parameter of the method call
   **/

  public TraceHandler(Object t){
    target = t;
  }

  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {

     // print implicit argument
     System.out.print(target);

     // print method name
     System.out.print("." + m.getName() + "(");

     // print explicit arguments
     if (args != null){
       for (int i = 0; i < args.length; i++){
         System.out.print(args[i]);
         if (i < args.length - 1)
           System.out.print(", ");
       }
     }

     System.out.println(")");

     // invoke actual method
     return m.invoke(target, args);

  }

}

谁能告诉我这个代理设计模式是怎么回事,它在这个特定程序中有什么作用及其优点?

让我们去除所有术语的花招,并通过示例在现实世界中理解它-

  1. 鲍勃和他的 10,000 个朋友去看足球比赛。
  2. 出于安全原因,入口处的保安必须对所有人员进行检查,然后才让他们进入。
  3. 体育场内也有比萨饼和啤酒摊位,但您必须持有效门票才能购买。
  4. 既然我们知道守卫在那里值班是正确的,没有有效门票的人不能进入体育场,比萨饼和啤酒供应商就不必自己检查每个人的门票了。

现在让我们看看-

  1. 守卫充当体育场摊位与世界其他地方之间的代理层。
  2. 如果没有警卫,披萨和啤酒供应商都需要编写检票代码,或者至少自己调用这段代码。这可能会在将来引起问题,因为如果体育场内开了一个新摊位而他们忘记调用检查人们门票的代码段怎么办。

优势-

  1. 代理允许您对对象执行某些操作,使用您正在使用的对象,同时允许您轻松编写更清晰、可靠的代码。
  2. 如果这是真实世界的场景,我既不会告诉警卫体育场内的摊位,也不会告诉供应商体育场的整个售票系统。这 2 个操作可以自行独立增长​​。

这两篇文章很好地介绍了代理-

代理 classes 在处理来自其他团队或第三方的代码时非常有用,可用于各种诊断或增强操作。

我已经将它们与数据库供应商一起使用 JDBC 连接 jar 和容错远程服务器调用,其中代理可以处理错误或重新连接的故障转移,以便应用程序逻辑在所有客户端调用中更清晰代码。

您的 TraceHandler 可以是自包含的 class 通过添加以下方法:

@SuppressWarnings("unchecked")
public static <T> T create(final T impl, final Class<?>... interfaces)
{
    final Class<?> cls = impl.getClass();

    return (T)Proxy.newProxyInstance(cls.getClassLoader(), interfaces, new TraceHandler(impl));
}

然后您可以使用 TraceHandler 来监视/记录您的应用程序使用的任何接口:

SomeObject x = TraceHandler.create(x, SomeObject.class);
FileVisitor myvisitor = TraceHandler.create(visitor, FileVisitor.class)

希望代理 classes 的作用会更加清晰,示例如下:

public class ProxyTest
{
    public static void main(String[] args) {

        var elements = new Integer[1000];

        for (int i = 0; i < elements.length; i++) {
            elements[i] = Integer.valueOf(i);
        }

        // construct a random integer
        Integer key = new Random().nextInt(elements.length) + 1;

        Comparator<Integer> comparator = Integer::compare;
        Comparator<Integer> comparator2 = TraceHandler.create(comparator, Comparator.class);

        // search for the key without proxy
        System.out.println("Search for "+key+" without proxy:");
        int result = Arrays.binarySearch(elements, key, comparator);

        // print match
        System.out.println("Found result="+result);

        // search for the key with proxy prints debug info per call
        System.out.println("Search "+key+" with proxy:");
        int result2 = Arrays.binarySearch(elements, key, comparator2);
        System.out.println("Found result2="+result2);
     }
}

上面的示例输出 运行 表明代理 class 可以打印每个比较调用的详细信息。

Search for 486 without proxy:
Found result at 486
Search 486 with proxy:
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(499, 486)
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(249, 486)
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(374, 486)
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(436, 486)
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(467, 486)
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(483, 486)
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(491, 486)
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(487, 486)
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(485, 486)
Whosebug.proxy.ProxyTest$$Lambda/0x0000000800c00a00@15aeb7ab.compare(486, 486)
Found result at 486

非常广泛的问题:

对此有几种不同的选择:

  • 代理模式 - 一种由另一个思想第三者操纵一个对象的方式
  • 延迟加载也可以成为该讨论的一部分
  • 终于最受欢迎了——编译后的动态代理和代码增强。很多著名的框架都是这样工作的(例如 spring、hibernate、selenium)。这允许实现更易读的代码并提高其质量(更少的错误)。动态代理带来动态惰性初始化的可能性、代码增强、更多声明性代码

例如spring 工作中的事务注释

class UsersDao {
@Transactional
  public void method() {
    // DO SOME STUFF
  }
}

Spring 正在创建 "extends" UsersDao 的动态代理,但实际上确实将所有方法调用重定向到实现 InvocationHandler 接口的特定对象

InvocationHandler 示例

public interface InvocationHandler {

    /**
     * Processes a method invocation on a proxy instance and returns
     * the result.  This method will be invoked on an invocation handler
     * when a method is invoked on a proxy instance that it is
     * associated with.
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

"invoke" spring 内部正在做类似的事情:

Transaction tx = TransactionManager.createTransaction()
try {
    // execute method
    method.invoke();
    tx.commit()

}
catch (Exception e) {
   // execute callback 
    tx.rollback()
}
finally () {
   // do clean up
   // tx.flush()
}

这是通过动态代理的魔法实现的

动态代理工具:

https://github.com/cglib/cglib/wiki

https://www.javassist.org/

https://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html

代理 classes 充当访问对象的接口,通常添加额外的功能。

它们有点类似于 Wrapper classes,但它们不包含与之交互的对象,当您必须向 class 添加额外的行为时需要它们,您可以'更改代码。