第59天:理解Java中的代理和动态代理
#java #proxy #100daysofjava

代理人

代理是一种设计模式。当我们要添加或更改现有类的某些功能时,我们创建和使用代理对象。代理对象而不是原始对象。通常,代理对象具有与原始对象相同的方法。在Java代理类中,通常会扩展原始类。代理具有原始对象的句柄,可以在其上调用该方法。

代理以其最通用的形式,是作为其他事物接口的类。在外行的术语中,Java中的代理班是一个班级,代表另一个班级委派了责任。对象,代理模仿称为实现对象。

Java中的代理类型

Java中主要有两种代理

  1. 静态代理
  2. 动态代理

静态代理

手动编写的代理称为静态代理。下面的示例是STATC代理

首先,我们可以创建一个界面,要在代理和真实类之间共享。

public interface User {
    String getType();
}

现在我们有了一个接口,我们可以实现特定用户。

public class FreeUser implements User {
    public String getType() {
        return "free";
    }
}

现在,我们可以创建一个Proxy接口,该接口将扩展用户界面以获取可以通过其实现来代理的行为。

public interface ProxyUser extends User {
}

因此,可以通过实现ProxyUser
实现FreeUserProxy

public class FreeUserProxy implements ProxyUser {
    private FreeUser freeUser = new FreeUser();
    private static final Logger log = LoggerFactory.getLogger(FreeUserProxy.class);

    public String getType() {
        log.info("getType() called");
        return freeUser.getType();
    }
}

现在从主类运行代码

public class Day59 {
    public static void main(String [] args) {
        User user = new FreeUserProxy();
        System.out.println(user.getType());
    }
}

用法

当我们需要创建包装器以涵盖客户端的主要对象复杂性时,使用

代理模式。此外,我们可以在代理对象上添加其他行为,以增强代理对象。

优点

  1. 代理可以隐藏复杂的任务,例如进行网络通信,交易管理而无需更改实施。

  2. 代理可用于在没有更改实现对象的顶部插入自定义行为/代码。有时,外部库的代码无法访问编辑,可以将自定义行为插入该库提供的方法的预/后执行。例如,您可以为java.net.httpurlconnection类编写代理,以记录所有外部服务呼叫请求,而无需更改httpurlConnection。

  3. 代理模式的其他优点之一是安全性。远程代理可用于在客户端中提供代理存根,并在服务器上调用实现。

缺点

有时静态代理可能违反干原理,e.x静态代理类定义非常特定于实现,这意味着对于每个实施,代理需要明确定义,并且是重复的工作。

考虑一个方案,您必须实现代理以计算多个类的方法调用。如果您使用的是静态代理,则一遍又一遍地定义具有重复逻辑的多个代理类。

在上面的代理示例中,我们正在使用单行计数方法调用。如果代理有100行代码可以在数据库中持续一个数据,并且仅在一行中发现一个错误,则必须记住在整个数十个甚至数百个,均匀的代码中更改该行其他代理。

动态代理

动态代理是代理设计模式,其中在运行时动态创建代理对象。

代理设计模式使用代理。它充当客户和基础真实对象之间的调解人。程序员可以在将请求委派给真实对象之前在代理中执行访问控制,验证和其他操作。

形成static代理的缺点,如果我们在运行时以某种方式可以根据客户端的呼叫创建一个代理对象,然后在将呼叫授权到真实对象之前执行通用操作(在我们的情况下记录动作)?好吧,这就是动态代理的作用。

动态代理的过程如下:

  1. 客户端调用对象上的某些操作。
  2. 系统在运行时创建一个基于客户端呼叫的运行时代理对象。
  3. 代理对象调用通用方法,以执行每个调用时执行通用操作。
  4. 在操作之后,代理对象将呼叫委托给真实对象。

简而言之,如果您有一些通用操作要执行,请使用动态代理,但是如果您希望每个类都受到不同的处理(在某些类中,在某些类中执行登录,在某些访问中,则在某些访问控件中不,请使用。 )使用简单的代理。

现在,要在Java中创建动态代理,我们可以使用Java反射在运行时创建接口的动态实现。通过使用类java.lang.reflect.Proxy

动态代理可用于许多不同的目的,例如数据库连接和交易管理,用于单元测试的动态模拟对象以及其他类似AOP的方法拦截目的。

我们使用Proxy.newProxyInstance()方法创建动态代理。 newproxyinstance()方法采用3个参数:

  1. load 动态代理类。
  2. 一个要实现的接口数组。
  3. 一个调用者将所有方法转发代理人调用。

例如

InvocationHandler handler = new UserInvocationHandler();
User proxy = (User) Proxy.newProxyInstance(
                            User.class.getClassLoader(),
                            new Class[] { User.class },
                            handler);

运行此代码后,代理变量包含Vehicle接口的动态实现。对代理的所有调用都将转发给常规InvocationHandler接口的处理程序实现。

InvocationHandler

如前所述,您必须将InvocationHandler实现传递给Proxy.newProxyInstance()方法。所有方法调用动态代理都将转发到此InvocationHandler实现。这是InvocationHandler界面的外观:

public interface InvocationHandler{
  Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable;
}

传递给invoke()方法的代理参数是实现接口的动态代理对象。通常您不需要此对象。

Method对象传递到Invoke()方法中表示在接口上调用动态代理工具的方法。从方法对象您可以获取方法名称,参数类型,返回类型等。

调用了实现接口中的方法时,Object[] args数组包含传递给代理的参数值。注意:已实现的接口中的原语(int,长等)包裹在其对象(整数,长等)中。

现在,例如编写一个GenericLogger类,该类将通过拦截方法调用来记录方法调用。


 public class GenericLogger implements InvocationHandler {
    private Object target;
    public GenericLogger(Object target) {
        this.target = target;
    }
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        System.out.println("Generic Logger Entry: Invoking " +
        m.getName());
        return m.invoke(target, args);
    }
 }

现在调用主要类别的代理

public class Day59 {
    public static void main(String[] args) {
        User user = new FreeUser();
        ClassLoader cl = User.class.getClassLoader();
        User u = (User) Proxy.newProxyInstance(cl,
                new Class[] {User.class}, new GenericLogger(c));
        u.getType();
    }
}

已知用例

动态代理至少用于以下目的:

  1. 数据库连接和交易管理
  2. 单元测试的动态模拟对象
  3. 将DI容器改编为自定义工厂界面
  4. 类似AOP的方法拦截

优点

它可以用作装饰器,以提供一些额外的能力来代理对象和所有其他静态代理,而不是其缺点。

缺点

它使用Java反射API,该API的性能和对象创建往往很慢。如果没有很好地了解动态代理在运行时的工作和创建代理,也会感到神奇