> Java > Java베이스 > Java 동적 프록시의 원리

Java 동적 프록시의 원리

풀어 주다: 2019-11-29 17:06:40
앞으로
2058명이 탐색했습니다.

Java 동적 프록시의 원리

Java 동적 프록시 메커니즘의 출현으로 Java 개발자는 수동으로 프록시 클래스를 작성하지 않고도 간단히 인터페이스 세트를 지정하고 클래스 객체를 위임할 수 있습니다. (권장: java 비디오 튜토리얼)

프록시 클래스는 리플렉션 실행을 위해 대리자 개체에 대한 모든 메서드 호출을 디스패치하는 역할을 담당합니다. 디스패치 실행 프로세스 중에 개발자는 필요에 따라 대리자 클래스 개체와 해당 기능을 조정할 수도 있습니다. 이는 매우 유연하고 유연한 에이전시 프레임워크입니다. 다음으로 동적 에이전트에 대해 배우기 시작합니다.

동적 프록시에 대한 간략한 설명

Java의 동적 프록시 메커니즘에는 두 가지 중요한 클래스 또는 인터페이스가 있는데, 하나는 InvocationHandler(인터페이스)이고 다른 하나는 Proxy(클래스)입니다.

1. 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
로그인 후 복사

이 메소드는 세 개의 매개변수를 수신하고 각각의 의미는 다음과 같습니다.

프록시: 우리가 프록시하는 실제 객체를 나타냅니다.

method: 실제 객체의 메소드를 호출하려는 Method 객체를 의미합니다.

args: 실제 객체의 메소드를 호출할 때 허용되는 매개변수를 의미합니다.

반환된 Object는 의 반환 유형을 나타냅니다. 실제 객체 방법 위의 내용은 다음 예에서 자세히 이해됩니다.

the value to return from the method invocation on the proxy instance.
로그인 후 복사

2. Proxy(클래스) 설명:

Proxy는 동적 프록시 클래스 및 인스턴스를 생성하기 위한 정적 메서드를 제공하며 해당 메서드에 의해 생성된 모든 동적 프록시 클래스의 상위 클래스이기도 합니다. 프록시 객체를 동적으로 생성하는 데 사용됩니다. 우리는 종종 newProxyInstance 메소드를 사용합니다:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
로그인 후 복사

매개변수 이해:

// 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
loader - the class loader to define the proxy class  
// 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口
interfaces - the list of interfaces for the proxy class to implement 
// 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
h - the invocation handler to dispatch method invocations to
로그인 후 복사

반환 결과 이해: 프록시 객체의 인스턴스

지정된 클래스 로더에 의해 정의된 프록시 클래스의 지정된 호출 핸들러가 있는 프록시 인스턴스

Simple Java Agent

동적 에이전트를 테스트하고 이해하기 위해 Java 프로젝트를 생성합니다. 프로젝트 구조는 다음과 같습니다.

Java 동적 프록시의 원리 1. 먼저 인터페이스를 정의하고 두 가지 메소드를 추가합니다. .

package com.huhx.proxy;

public interface Interface {
    void getMyName();

    String getNameById(String id);
}
로그인 후 복사
2. 위 인터페이스 RealObject를 구현하는 실제 클래스 정의:

package com.huhx.proxy;

public class RealObject implements Interface {
    @Override
    public void getMyName() {
        System.out.println("my name is huhx");
    }

    @Override
    public String getNameById(String id) {
        System.out.println("argument id: " + id);
        return "huhx";
    }
}
로그인 후 복사
3. 위 인터페이스 인터페이스도 구현하는 프록시 객체 정의:

package com.huhx.proxy;

public class SimpleProxy implements Interface {
    private Interface proxied;

    public SimpleProxy(Interface proxied) {
        this.proxied = proxied;
    }

    @Override
    public void getMyName() {
        System.out.println("proxy getmyname");
        proxied.getMyName();
    }

    @Override
    public String getNameById(String id) {
        System.out.println("proxy getnamebyid");
        return proxied.getNameById(id);
    }
}
로그인 후 복사
4. Main 메서드의 SimpleMain , 위 결과를 테스트합니다.

package com.huhx.proxy;

public class SimpleMain {
    private static void consume(Interface iface) {
        iface.getMyName();
        String name = iface.getNameById("1");
        System.out.println("name: " + name);
    }

    public static void main(String[] args) {
        consume(new RealObject());
        System.out.println("========================================================");
        consume(new SimpleProxy(new RealObject()));
    }
}
로그인 후 복사
5. 실행 결과는 다음과 같습니다.

my name is huhx
argument id: 1
name: huhx
========================================================
proxy getmyname
my name is huhx
proxy getnamebyid
argument id: 1
name: huhx
로그인 후 복사

Java의 동적 프록시

위의 간단한 Java 프록시를 완료한 후 이제 Java의 동적 프록시를 배우기 시작합니다. 동적으로 프록시를 생성하고 프록시된 메소드에 대한 호출을 동적으로 처리할 수 있기 때문에 프록시 개념보다 한 단계 더 나아갑니다. 동적 프록시에서 이루어진 모든 호출은 호출 유형을 공개하고 적절한 대응책을 결정하는 작업을 수행하는 단일 호출 처리기로 리디렉션됩니다. 아래에서는 Java 동적 프록시에 대한 이해를 심화하기 위해 사례를 사용합니다.

1. InvocationHandler를 상속하는 프로세서 생성: DynamicProxyHandler

package com.huhx.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

public class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    public DynamicProxyHandler(Object proxied) {
        System.out.println("dynamic proxy handler constuctor: " + proxied.getClass());
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("dynamic proxy name: " + proxy.getClass());
        System.out.println("method: " + method.getName());
        System.out.println("args: " + Arrays.toString(args));
        
        Object invokeObject = method.invoke(proxied, args);
        if (invokeObject != null) {
            System.out.println("invoke object: " + invokeObject.getClass());
        } else {
            System.out.println("invoke object is null");
        }
        return invokeObject;
    }
}
로그인 후 복사
2. 테스트 Main 메서드인 DynamicProxyMain을 작성합니다:

package com.huhx.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.huhx.proxy.Interface;
import com.huhx.proxy.RealObject;

public class DynamicProxyMain {
    public static void consumer(Interface iface) {
        iface.getMyName();
        String name = iface.getNameById("1");
        System.out.println("name: " + name);
    }

    public static void main(String[] args) throws Exception, SecurityException, Throwable {
        RealObject realObject = new RealObject();
        consumer(realObject);
        System.out.println("==============================");

        // 动态代理
        ClassLoader classLoader = Interface.class.getClassLoader();
        Class<?>[] interfaces = new Class[] { Interface.class };
        InvocationHandler handler = new DynamicProxyHandler(realObject);
        Interface proxy = (Interface) Proxy.newProxyInstance(classLoader, interfaces, handler);

        System.out.println("in dynamicproxyMain proxy: " + proxy.getClass());
        consumer(proxy);
    }
}
로그인 후 복사
3 실행 결과는 다음과 같습니다.

my name is huhx
argument id: 1
name: huhx
==============================
dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
in dynamicproxyMain proxy: class com.sun.proxy.$Proxy0
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getNameById
args: [1]
argument id: 1
invoke object: class java.lang.String
name: huhx
로그인 후 복사
위 출력 결과에서 다음과 같은 결론을 내릴 수 있습니다.

프록시 객체와 연결된 InvocationHandler는 프록시 객체가 메서드를 호출할 때만 해당 호출 메서드를 실행합니다.

invoke 이해 세 가지 매개변수: 객체 프록시는 프록시의 객체이고, Method method는 실제 객체에서 메소드를 호출하기 위한 Method 클래스이고, Object[] args는 실제 객체에서 메소드를 호출하기 위한 매개변수

원리 Java 동적 프록시

1. 동적 프록시의 핵심 코드는 Proxy.newProxyInstance(classLoader, 인터페이스, 핸들러)입니다. 소스 코드를 따라가서 살펴보겠습니다.

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
  // handler不能为空
    if (h == null) {
        throw new NullPointerException();
    }

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
  // 通过loader和接口,得到代理的Class对象
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
            // create proxy instance with doPrivilege as the proxy class may
            // implement non-public interfaces that requires a special permission
            return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Object run() {
                    return newInstance(cons, ih);
                }
            });
        } else {
       // 创建代理对象的实例
            return newInstance(cons, ih);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    }
}
로그인 후 복사
2. newInstance 메소드의 소스 코드:

private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
    try {
        return cons.newInstance(new Object[] {h} );
    } catch (IllegalAccessException | InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString());
        }
    }
}
로그인 후 복사
3 프록시 객체를 통해 메소드를 호출하면 이 메소드의 호출이 InvocationHandler 인터페이스의 호출 메소드로 전달됩니다.

소스코드에서 이 문장을 구현한 코드를 찾을 수 없어서 테스트 클래스의 메인 메소드에 다음 코드를 추가했습니다.

if (proxy instanceof Proxy) {
    InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy);
    invocationHandler.invoke(proxy, realObject.getClass().getMethod("getMyName"), null);
    System.out.println("--------------------------------------");
}
로그인 후 복사

这段代码的输出结果如下,与上述中调用代理对象中的getMyName方法输出是一样的,不知道Jvm底层是否是这样判断的:

dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
--------------------------------------
로그인 후 복사

更多java知识请关注java基础教程栏目。

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

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