> Java > java지도 시간 > 본문

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

黄舟
풀어 주다: 2017-09-08 09:46:50
원래의
1251명이 탐색했습니다.

이 글은 주로 Java 동적 프록시 메커니즘의 자세한 예에 대한 관련 정보를 소개합니다. 이 글을 통해 누구나 동적 프록시 메커니즘을 익힐 수 있기를 바랍니다. 필요한 친구는 이를 참조할 수 있습니다.

Java 동적 프록시 메커니즘

Spring을 배울 때 우리는 Spring이 주로 IoC와 AOP라는 두 가지 주요 아이디어를 가지고 있다는 것을 알고 있습니다. AOP Function을 통해 필요한 사항을 파악하기 위해 우리가 더 배워야 할 것은 기본 원리이며, 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: 실제 객체의 메소드를 호출할 때 허용되는 매개변수를 나타냅니다.

If 잘 이해하지 못하실 수도 있으므로 나중에 예제를 통해 이러한 매개변수에 대해 자세히 설명하겠습니다.

다음으로 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对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
로그인 후 복사

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

먼저 주제 유형 인터페이스를 정의하고 두 가지 메서드를 선언합니다.

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);
  }
}
로그인 후 복사

다음으로 위와 같이 동적 프록시 클래스를 정의합니다. 직설적으로 말하면 모든 동적 프록시 클래스는 InvocationHandler 인터페이스를 구현해야 하므로 동적입니다. 프록시 클래스도 예외는 아닙니다.

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;
  }

}
로그인 후 복사

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

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 유형도 아니고 우리가 정의한 인터페이스 집합의 유형도 아닙니다. Run은 동적으로 생성된 객체이며, 네이밍 방식은 이런 형태로 $로 시작하며, 중간에 Proxy가 있고, 마지막 숫자는 객체의 레이블을 나타낸다.

그럼 이 두 문장을 살펴보겠습니다

subject.rent();
subject.hello(“world”);
로그인 후 복사

다음은 프록시 객체를 통해 구현된 인터페이스의 메소드입니다. 이때 프로그램은 이 프록시 객체와 연관된 핸들러로 점프하여 호출합니다. 실행되고 핸들러 객체는 RealSubject 유형 매개변수를 받아들입니다. 이는 프록시하려는 것이 이 실제 객체임을 의미하므로 이때 핸들러의 호출 메소드가 실행을 위해 호출됩니다.

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中的动态代理机制,这个知识点非常非常的重要,包括我们Spring的AOP其就是通过动态代理的机制实现的,所以我们必须要好好的理解动态代理的机制。

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

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿