>  기사  >  Java  >  Java의 동적 프록시 메커니즘에 대한 자세한 소개

Java의 동적 프록시 메커니즘에 대한 자세한 소개

黄舟
黄舟원래의
2017-09-20 10:41:071092검색

이 기사는 주로 Java의 동적 프록시 메커니즘에 대한 예제 설명에 대한 정보를 소개합니다. 이 기사를 통해 모든 사람이 동적 프록시 메커니즘을 이해하고 숙달할 수 있기를 바랍니다. Java의 프록시 메커니즘

Spring을 배울 때 우리는 Spring에 주로 두 가지 주요 아이디어가 있다는 것을 알고 있습니다. 하나는 IoC이고 다른 하나는 AOP입니다. IoC의 경우 Spring의 핵심 AOP에 대해서는 말할 것도 없습니다. AOP를 전달하는 방법이 우리의 기능을 충족하는지 알아야 하지만 더 배워야 할 것은 기본 원칙입니다. AOP의 원리는 Java의 동적 프록시 메커니즘이므로 이 에세이는 Java의 동적 메커니즘에 대한 리뷰입니다.

Java의 동적 프록시 메커니즘에는 두 가지 중요한 클래스 또는 인터페이스가 있습니다. 하나는 InvocationHandler(인터페이스)이고 다른 하나는 프록시(클래스)입니다. 이 클래스와 인터페이스는 동적 프록시를 구현하는 데 필요합니다. 먼저 Java의 API 도움말 문서에서 이 두 클래스를 어떻게 설명하는지 살펴보겠습니다.

InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

모든 동적 프록시 클래스는 InvocationHandler 인터페이스를 구현해야 하며 클래스의 각 프록시 인스턴스는 핸들러와 연결됩니다. 프록시 객체를 통해 메소드를 호출하면 이 메소드의 호출이 InvocationHandler 인터페이스의 호출 메소드로 전달됩니다. InvocationHandler 인터페이스의 유일한 메소드인 호출 메소드를 살펴보겠습니다.

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

이 메소드는 총 3개의 매개변수를 허용하는데, 이 3개의 매개변수는 무엇을 나타냅니까?

proxy: 우리가 프록시하고 있는 실제 개체를 나타냅니다.
  • method: 실제 개체의 메서드를 호출하려는 Method 개체를 나타냅니다.
  • args: 실제 개체를 호출하는 메서드를 나타냅니다. 특정 메소드

  • 에서 허용하는 매개변수를 이해하지 못하는 경우 나중에 예제를 통해 이러한 매개변수에 대해 자세히 설명하겠습니다.
다음으로 Proxy 클래스를 살펴보겠습니다.

Proxy provides static methods for creating dynamic proxy classes and instances, and it is
 also the superclass of all dynamic proxy classes created by those methods.

Proxy 클래스의 기능은 다양한 메서드를 제공하지만 가장 많이 사용하는 메서드는 newProxyInstance 메서드입니다.

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
 throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method
 invocations to the specified invocation handler.

이 메소드의 기능은 세 개의 매개변수를 받는 동적 프록시 객체를 가져오는 것입니다. 이 세 매개변수가 무엇을 나타내는지 살펴보겠습니다.


loader: 어떤 ClassLoader 객체가 사용되는지 정의된 ClassLoader 객체입니다. 생성된 프록시 객체를 로드합니다

  • 인터페이스: 프록시에 필요한 객체에 어떤 인터페이스를 제공할지 나타내는 인터페이스 객체 배열입니다. It에 인터페이스 세트를 제공하면 이 프록시 객체가 인터페이스를 구현한다고 주장합니다. (다형성), 이 인터페이스 세트의 메소드를 호출할 수 있도록

  • h: InvocationHandler 객체. 이는 동적 프록시 객체가 메소드를 호출할 때 InvocationHandler 객체가 이에 연결됨을 의미합니다.

  • 좋아요 , 이 두 인터페이스(클래스)를 소개한 후 동적 프록시 모드가 어떤 모습인지 알아보기 위해 예제를 살펴보겠습니다.

  • 먼저 Subject 유형 인터페이스를 정의하고 이에 대한 두 가지 메서드를 선언합니다.


public interface Subject
{
  public void rent();

  public void hello(String str);
}

그런 다음 이 인터페이스를 구현하기 위한 클래스를 정의합니다. 이 클래스는 실제 객체인 RealSubject 클래스입니다.


public class RealSubject implements Subject
{
  @Override
  public void rent()
  {
    System.out.println("I want to rent my house");
  }

  @Override
  public void hello(String str)
  {
    System.out.println("hello: " + str);
  }
}

다음 단계는 앞서 언급한 것처럼 동적 프록시 클래스를 정의하는 것입니다. 인터페이스이므로 동적 프록시 클래스도 예외는 아닙니다.


public class DynamicProxy implements InvocationHandler
{
  // 这个就是我们要代理的真实对象
  private Object subject;

  //  构造方法,给我们要代理的真实对象赋初值
  public DynamicProxy(Object subject)
  {
    this.subject = subject;
  }

  @Override
  public Object invoke(Object object, Method method, Object[] args)
      throws Throwable
  {
    //  在代理真实对象前我们可以添加一些自己的操作
    System.out.println("before rent house");

    System.out.println("Method:" + method);

    //  当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
    method.invoke(subject, args);

    //  在代理真实对象后我们也可以添加一些自己的操作
    System.out.println("after rent house");

    return null;
  }

}

마지막으로 클라이언트 클래스를 살펴보겠습니다.


/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public class Client
{
  public static void main(String[] args)
  {
    //  我们要代理的真实对象
    Subject realSubject = new RealSubject();

    //  我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
    InvocationHandler handler = new DynamicProxy(realSubject);

    /*
     * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
     * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
     * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
     * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
     */
    Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
        .getClass().getInterfaces(), handler);

    System.out.println(subject.getClass().getName());
    subject.rent();
    subject.hello("world");
  }
}

먼저 콘솔 출력을 살펴보겠습니다.


$Proxy0

before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.rent()
I want to rent my house
after rent house

before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
hello: world
after rent house

먼저 $Proxy0을 살펴보겠습니다. 이 내용은 System.out.println(subject.getClass().getName());에 의해 생성됩니다. 그러면 프록시 개체의 클래스 이름은 무엇입니까? 이대로 돌아가?


Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
        .getClass().getInterfaces(), handler);

아마도 반환된 프록시 객체가 Subject 유형 객체이거나 InvocationHandler 객체일 것이라고 생각했지만 그렇지 않았습니다. 먼저 이를 Subject 유형 객체로 변환할 수 있는 이유를 설명하겠습니다. 여기? 그 이유는 newProxyInstance 메소드의 두 번째 매개변수에서 이 프록시 객체에 대한 인터페이스 세트를 제공한 다음 내 프록시 객체가 이 인터페이스 세트를 구현하기 때문입니다. 물론 이 프록시의 유형 변환을 강제로 수행할 수 있습니다. 이 인터페이스 세트 중 하나입니다. 여기의 인터페이스는 주제 유형이므로 주제 유형으로 변환될 수 있습니다.

同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

接着我们来看看这两句


subject.rent(); 
subject.hello(“world”);

这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行:


/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public Object invoke(Object object, Method method, Object[] args)
      throws Throwable
  {
    //  在代理真实对象前我们可以添加一些自己的操作
    System.out.println("before rent house");

    System.out.println("Method:" + method);

    //  当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
    method.invoke(subject, args);

    //  在代理真实对象后我们也可以添加一些自己的操作
    System.out.println("after rent house");

    return null;
  }

我们看到,在真正通过代理对象来调用真实对象的方法的时候,我们可以在该方法前后添加自己的一些操作,同时我们看到我们的这个 method 对象是这样的:


public abstract void com.xiaoluo.dynamicproxy.Subject.rent()

public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)

正好就是我们的Subject接口中的两个方法,这也就证明了当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。

这就是我们的java动态代理机制。

위 내용은 Java의 동적 프록시 메커니즘에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.