Java에서 동적 프록시를 구현하는 핵심은 프록시와 호출 핸들러의 두 가지입니다. InvocationHandler 인터페이스의 Invoke 메소드부터 시작하고 Java가 동적 프록시를 구현하는 방법을 간략하게 설명하겠습니다.
먼저, 호출 방법의 전체 형태는 다음과 같습니다.
공개 객체 호출 (개체 프록시, 메소드 메소드, Object [] args)는 던질 수있는 {method.invoke (obj, args); return null;}먼저, 그 방법이 메소드라고 불리는 방법, 즉 실행 해야하는 방법이라고 추측 해 보자. Args는 방법의 매개 변수입니다. 프록시,이 매개 변수는 무엇입니까? 상기 invoke () 메소드의 구현은 비교적 표준 형식입니다. 여기에는 프록시 매개 변수가 사용되지 않습니다. 다음과 같이 JDK 문서의 프록시 설명을 확인하십시오.
프록시 인터페이스 중 하나를 통한 프록시 인스턴스의 메소드 호출은 인스턴스의 호출 핸들러의 호출 메소드로 발송됩니다. 프록시 인스턴스를 전달한 java.lang.reflect.method 객체 호출 된 메소드 및 인수를 포함하는 유형 객체의 배열을 식별합니다.
이것으로부터 우리는 위의 추측이 정확하다는 것을 알 수 있으며, 프록시 매개 변수가 프록시 클래스의 인스턴스라는 것을 알고 있습니다.
설명의 편의를 위해 동적 프록시를 구현하는 간단한 예입니다.
// 초록 역할 (Dynamic Proxy는 프록시 인터페이스 만 가능) 공개 인터페이스 주제 {public void request (); } // 실제 역할 : 요청 () 주제 공개 클래스 realSubject 구현 주제 {public void request () {system.out.println ( "실제 주제에서"); }} // invocationHandler 공개 클래스 DynamicSubject 구현 invocationHandler {private 객체 obj; // 이것은 동적 프록시의 이점입니다. 캡슐화 된 객체는 객체 유형이며 모든 유형의 객체를 공개 DynamicSubject () {} public dynamicsubject (object obj) {this.obj = obj; } //이 메소드는 공개 객체 호출 (개체 프록시, 메소드 메소드, Object [] args)를 던지기 위해 표시하는 것이 아닙니다. method.invoke (obj, args); System.out.println ( "호출 후" + 메소드); 널 리턴; }} // 클라이언트 : 프록시 인스턴스를 생성하고 요청 () 메서드를 호출하십시오 () 공개 클래스 클라이언트 {public static void main (String [] args) 던지기 가능 {// todo 자동 생성 메소드 스텁 주제 rs = new RealSubject (); // 프록시 클래스 invocationHandler ds = new DynamicSubject (rs)를 지정합니다. 클래스 <?> cls = rs.getClass (); // 다음은 일회성 프록시 대상 과목의 일회성 생성입니다. (주제) proxy.newproxyInstance (cls.getClassLoader (), cls.getInterfaces (), ds); // 여기에서는 결과를 실행하여 주제가 프록시 인스턴스임을 증명할 수 있습니다. 이 인스턴스는 주제 인터페이스 시스템을 구현합니다. out.println (주제 인스턴스 프록시); // 여기에서 피사체의 클래스 클래스가 $ proxy0임을 알 수 있습니다. 이 $ proxy0 클래스는 프록시를 상속하고 주제 인터페이스 시스템을 구현합니다. System.out.print ( "주제의 속성은 다음과 같습니다."); field [] 필드 = subject.getClass (). getDeclaredFields (); for (field f : field) {system.out.print (f.getName ()+","); } system.out.print의 메소드 ( "/n"+"대상은 :"); 메소드 [] 메소드 = goudr.getClass (). getDeclaredMethods (); for (method m : method) {system.out.print (m.getName ()+","); } system.out.println의 상위 클래스 ( "/n"+"주제는 다음과 같습니다. System.out.print ( "/n"+"주제는 인터페이스를 구현합니다 :"); class <?> [] interfaces = goudr.getClass (). getInterfaces (); for (class <?> i : interfaces) {system.out.print (i.getName ()+","); } system.out.println ( "/n/n"+"run renate is :"); 대상 .request (); }} 작업 결과는 다음과 같습니다. 패키지 이름은 여기에서 생략됩니다. *** 대신
진실
주제의 클래스 클래스는 다음과 같습니다. 클래스 $ proxy0
대상의 특성은 다음과 같습니다. M1, M3, M0, M2,
주제의 방법은 다음과 같습니다. 요청, 해시 코드, 평등, 토스트 링,
부모 클래스의 주제는 다음과 같습니다. 클래스 java.lang.reflect.proxy
주제에 의해 구현 된 인터페이스는 다음과 같습니다. cn.edu.ustc.dynamicproxy.subject,
작동 결과는 다음과 같습니다.
공개 초록 void ***를 호출하기 전에.
실제 주제에서.
공개 초록 void ***를 호출 한 후.
추신 :이 결과에 대한 정보는 적어도 저에게 매우 중요합니다. 역동적 인 대리에서 현기증의 근본 원인은 위의 주제를 오해했다는 것입니다. 적어도 표면에 의해 혼란스러워서 피사체와 프록시 사이의 연결을 찾지 못했습니다. 한 번 마지막 통화 요청 ()이 invoke ()에 연결되는 방법에 대해 혼란 스러웠으며 요청이 존재한다는 것을 어떻게 알 수 있었는지 어떻게 알았습니까? 실제로, 위의 True 및 Class $ Proxy0은 많은 질문을 해결할 수 있으며 아래에 언급 될 $ proxy0의 소스 코드와 결합하여 동적 프록시의 의심을 완전히 해결할 수 있습니다.
위의 코드와 결과에서 볼 수 있듯이 invoke () 메소드를 호출하지 않았지만이 메소드가 실행되었습니다. 아래 전체 프로세스를 분석하겠습니다.
클라이언트의 코드에서 판단하면 NewProxyInstance 메소드를 획기적인 것으로 사용할 수 있습니다. 먼저 프록시 클래스의 NewProxyInstance 메소드의 소스 코드를 살펴 보겠습니다.
public static 객체 NewProxyInstance (클래스 로더 로더, 클래스 <?> [] 인터페이스, invocationHandler h) 불법 행위 값을 던진다 {if (h == null) {throw nullpointerexception (); } / * * 설계된 프록시 클래스를 찾거나 생성합니다. */ 클래스 cl = getProxyClass (로더, 인터페이스); / * * 설계된 호출 핸들러로 생성자를 호출하십시오. * / try { / * * 프록시 소스 코드에는 다음과 같은 정의가 있습니다. * CONS는 InvocationHandler 유형의 공식 매개 변수를 갖는 생성자 방법입니다.*/ 생성자 cons = cl.getConstructor (ConstructorParams); return (Object) cons.newinstance (new Object [] {h}); } catch (nosuchmethodexception e) {새 ninternerror (e.toString ()); } catch (delegalAccessException e) {Throw new InternalError (e.toString ()); } catch (InstantiationException e) {Throw New InternalError (e.toString ()); } catch (invocationTargeteXception e) {Throw new InternalError (e.toString ()); }} proxy.newproxyInstance (클래스 로더 로더, 클래스 <?> [] 인터페이스, invocationHandler h)는 다음을 수행합니다.
(1) 매개 변수 로더 및 인터페이스를 기반으로 메소드 getProxyClass (로더, 인터페이스)를 호출하고 프록시 클래스 $ proxy0. $ proxy0 클래스를 작성하고 인터페이스 인터페이스를 구현하고 프록시 클래스를 상속합니다.
(2) $ proxy0을 인스턴스화하고 DynamicSubject를 생성자에서 통과 한 다음 $ proxy0은 부모 클래스 프록시의 생성자를 호출하고 다음과 같이 h에 값을 할당합니다.
클래스 프록시 {invocationHandler h = null; 보호 프록시 (invocationHandler h) {this.h = h; } ...}Proxy의 $ proxy0을 상속하는 소스 코드를 살펴 보겠습니다.
공개 최종 클래스 $ proxy0 확장 프록시 구현 주제 {private static method m1; 개인 정적 방법 M0; 개인 정적 방법 M3; 개인 정적 방법 M2; static {try {m1 = class.forname ( "java.lang.object"). getMethod ( "equals", new class [] {class.forname ( "java.lang.object")); m0 = class.forname ( "java.lang.object"). getMethod ( "Hashcode", New Class [0]); m3 = class.forname ( "***. realSubject"). getMethod ( "요청", 새 클래스 [0]); m2 = class.forname ( "java.lang.object"). getMethod ( "Tostring", New Class [0]); } catch (nosuchmethodexception nosuchmethodexception) {새로운 nosuchmethoderror (nosuchmethodexception.getMessage ()); } catch (classNotFoundException classNotFoundException) {새 noclassDeffoundError (classNotFoundException.getMessage ()); }} // static public $ proxy0 (invocationHandler invocationHandler) {super (invocationHandler); } @override public final boolean equals (object obj) {try {return ((boolean)) super.h.invoke (this, m1, new Object [] {obj})) .booleanValue (); } catch (Throwable Throwable) {새로운 미등성 무적 외과 (Throwable); }} @override public final int hashcode () {try {return ((integer) super.h.invoke (this, m0, null)). intvalue (); } catch (Throwable Throwable) {새로운 미등성 무적 외과 (Throwable); }} public final void request () {try {super.h.invoke (this, m3, null); 반품; } catch (오류 e) {} catch (Throwable Throwable) {새로운 미등성 불동식 외출 (Throwable); }} @override public final string tostring () {try {return (string) super.h.invoke (this, m2, null); } catch (Throwable Throwable) {새로운 미등성 무적 외과 (Throwable); }}}그런 다음 결과 $ proxy0 인스턴스를 피사체에 시전하고 주제에 대한 참조를 할당하십시오. 대상 .request () 메소드가 실행되면 $ proxy0 클래스의 요청 () 메소드가 호출되고 부모 클래스 프록시의 h의 invoke () 메소드가 호출됩니다. 즉, invocationHandler.invoke ()입니다.
추신 : 1. 주목해야 할 한 가지는 프록시 클래스의 getProxyClass 메소드가 프록시의 클래스 클래스를 반환한다는 것입니다. 그 이유는 처음에 낮은 수준의 실수를했기 때문에 반환이 "프록시 클래스의 클래스 클래스"라고 생각했기 때문입니다! GetProxyClass의 소스 코드를 살펴 보는 것이 좋습니다. =
2. $ proxy0의 소스 코드에서, 동적 프록시 클래스는 디스플레이 정의 인터페이스의 메소드를 프록시 할뿐만 아니라 Java의 루트 클래스 객체에서 상속 된 세 가지 메소드 및이 세 가지 메소드만을 프록시한다는 것을 알 수 있습니다.
Q : 지금까지 여전히 의문이 있습니다. Invoke 메소드의 첫 번째 매개 변수는 프록시 인스턴스입니다 (정확하게, $ proxy0의 인스턴스가 최종적으로 사용됨), 사용은 무엇입니까? 아니면 프로그램에 기능이 어떻게 나타나나요?
A : 현재 레벨 에서이 프록시 매개 변수는 영향을 미치지 않습니다. 전체 동적 프록시 메커니즘에서는 호출 핸들러에서 호출 메소드의 프록시 매개 변수가 사용되지 않습니다. 전달 된 매개 변수는 실제로 프록시 클래스의 인스턴스입니다. 프로그래머가 호출 방법에서 반사를 사용하여 프록시 클래스에 대한 정보를 얻을 수 있다고 생각합니다.
요약
위는 invocationHandler의 invoke () 메소드에 대한이 기사의 전체 내용입니다. 모든 사람에게 도움이되기를 바랍니다. 관심있는 친구들은이 사이트를 계속 참조 할 수 있습니다.
스프링 정적 프록시 및 동적 프록시 코드에 대한 자세한 설명
스프링 프레임 워크 종속성 주입 방법 예
SpringMVC 간단한 로그인 예제의 Java 프로그래밍 구현
단점이 있으면 메시지를 남겨 두십시오.