JDK 동적 프록시에 대한 자세한 설명
이 기사는 주로 JDK Dynamic Proxy의 기본 원칙을 소개하여 모든 사람들이 JDK 프록시를 더 깊이 이해할 수 있도록하고 그것이 무엇인지 알 수 있습니다. JDK Dynamic Proxy의 진정한 원리와 미래에 JDK 프록시를 작성하면 데모를 확인하지 않고 완벽한 프록시를 작성할 수 있습니다. 먼저 간단한 데모를 수행하겠습니다.
JDK 프록시 Helloworld
Robin */Public Interface HelloWorld {void sayshello ();} Package com.yao.proxy;/** * com.yao.proxy; import com.yao.helloworld;/** * */public class helloworldimpl은 helloworld {public void sayhello () {System.out.print ( "Hello World"); package com.yao.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * Created by robin */public class MyInvocationHandler implements InvocationHandler{ private Object target; public MyInvocationHandler(Object target) { this.target=target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println ( "+ method.getName ()+"가 호출됩니다! com.yao.proxy; import com.yao.helloworld; import java.lang.reflect.constructor; import java.lang.reflect.invocationhandler; import java.lang.reflect.invocationtargetexception; import java.lang.reflect.proxy;/ */ */ */ */ */ */ */ */ */ */problic main (String [] args)은 nosuchmethodexception, 불법 행위 예고, InstantiationException {// 여기에 약간의 복잡한 글쓰기 방법을 사용하여 모든 사람들이 더 많은 것을 이해하는 데 도움이됩니다. class <?> proxy.getProxyClass (jdkproxytest.class.getClassloader (), HelloWorld.class); Illoworld) cons.newinstance (ih); // 다음은 본질적으로 위와 동일합니다 (); */}위의 코드를 실행하면 간단한 JDK 프록시가 구현됩니다.
에이전트 생성 프로세스
우리가 매일 JDK Dynamic Proxy라고 부르는 이유는이 프록시 클래스가 런타임에 우리를 위해 JDK에 의해 동적으로 생성되기 때문입니다. 프록시 생성 프로세스를 설명하기 전에 먼저 jvm 스타트 업 매개 변수에 따라 매개 변수 -dsun.misc.proxygenerator.savegeneratefiles = true를 추가합니다. Intellij Idea를 사용했으며 프록시 클래스가 생성 된 후 특정 패키지 이름을 디렉토리 구조로서 프로젝트의 루트 디렉토리에 직접 배치했습니다 .
프록시 클래스를 생성하는 프로세스에는 주로 두 부분이 포함됩니다.
프록시 클래스의 getProxyClass 메소드 항목 : 클래스 로더 및 인터페이스에 전달해야합니다.
그런 다음 getProxyClass0 메소드를 호출하면 현재 인터페이스를 구현하는 프록시 클래스가 존재하지 않으면 근거가 존재하지 않습니다. 여기서 인터페이스 인터페이스 수에는 65535를 초과 할 수없는 제한이 있음을 명확하게 볼 수 있습니다. ProxyClassCache의 특정 초기화 정보는 다음과 같습니다.
proxyClassCache = new 약점 <> (new keyfactory (), new proxyClassFactory ());
프록시 클래스를 생성하기위한 특정 논리는 proxyclassFactory의 적용 방법을 통해 생성됩니다.
ProxyClassFactory의 논리에는 패키지 이름의 생성 로직이 포함되어 있으며 프록시 클래스를 생성하고 프록시 클래스 바이트 코드를 JVM에로드합니다.
1. 기본 패키지 이름 생성 로직은 com.sun.proxy입니다. 프록시 클래스가 비공개 프록시 인터페이스 인 경우 기본 클래스 이름은 $ proxy와 자체적으로 구성된 정수 값입니다.
2. 패키지 이름이 준비된 후에는 프록시 크기에 따라 ProxyGenerator를 통해 생성됩니다. 프록시 클래스에서 모든 프록시 메소드 논리는 호출 메소드를 호출하는 것과 동일합니다.
3. 전달 된 클래스 로더를 통해 바이트 코드를 JVM에로드하십시오. defuleclass0 (로더, proxyname, proxyclassfile, 0, proxyclassfile.length);.
개인 정적 최종 클래스 ProxyClassFactory는 이중화를 구현합니다 <classloader, class <?> [], class <? >> {// preval static final string proxyclassnameprefix = "$ proxy"; 로더 로더, class <?> [] interfaces 세트 = new IdentityHashmap <> (class <?> intf : interfaces) { / * * interface <? getName (), false, loader)} catch (classNotFoundException e) {} if (Interfaceclass! = intf) {New ImperalargumentException (intf +는 클래스 로더에서 보이지 않습니다.) 얼굴은 복제가 아닙니다. */ if (Interfaceset.put (interfaceclass, boolean.true)! = null) { " + interfaceclass.getName ()}} packififier afubifier; * 프록시 클래스가 동일한 패키지로 정의되도록 XY 인터페이스 * 비공개 프록시 인터페이스는 동일한 패키지에 있습니다 pkg = ((n == -1)? ": name.substring (0, n + 1)); proxypkg = pkg} else if (! pkg.equals (proxypkg)) { "다른 패키지의 비 공개 인터페이스"); "."~ * * 프록시 클래스의 이름을 선택하십시오. * / long num = nexiqueNumber.getAndIncrement (); proxyname, 인터페이스, accessflags) {// jvm return definclass0에로드하십시오 (로더, proxyname, proxyclassfile, 0, procyclassfile.length)} catch (classformaterror e) { / * * Classformaterror는 ( * 프록시 클래스 세대 코드) 대리인 * 클래스 제작에 제공된 다른 *의 잘못된 측면이 있음을 의미합니다.프록시 클래스의 바이트 코드를 기반으로 디 컴파일 할 수 있으며 다음 결과는 Sayhello 메소드 만 가지고 있지만, 대상, ToString 및 Hashcode의 세 가지 메소드가 포함됩니다.
에이전트의 거친 구조에는 4 개의 부분이 포함됩니다.
com.sun.proxy; import com.yao.helloworld; import java.lang.reflect.invocationHandler; import java.lang.reflect.method; import java.lang.reflect.proxy; import java.lang.reflect. 개인 정적 방법 M3; Public $ proxy0 (var1)은 {객체 var1) {return (boolean) super.h.invoke (this, m1, new object [] {var1}) laredthrowableException (var4)} public final void sayhello () trows {super.h.invoke (this, m3, (Object [])} catch (runtimeexception | rustable var3) {throw newlaredthrowableexcept (var3) {return (return) (string). } catch (runtimeexception | error var2) {trow var2} catch (Throwable var3) {새로운 노동자 hashcode (var3); 3);} 정적 {try {m1 = class.forname ( "java.lang.object". getMethod ( "equals", new class [] { "java.lang.object"}); 메소드 ( "Tostring", New Class [0]); M0 = Class.forname ( "java.lang.object". GetMethod ( "Hashcode") Class [0]; Catch (Nosuchmethodexception var2) {새 nosuchmethoderror (var2.getMessage ()) {trans noclassDeffoundError (var3.getMessage ());FAQ :
1. TOSTRING () HASHCODE () 동등한 () 메소드 호출 로직 :이 세 객체의 메소드가 호출되면 다른 인터페이스 메소드 및 메소드와 마찬가지로 호출 핸들러 로직을 통과합니다. 객체의 다른 메소드는 프록시 처리 로직을 따르지 않지만 프록시가 상속받은 객체의 메소드 로직을 직접 따릅니다.
2. 인터페이스에 평등 및 ToString 해시 코드 메소드가 포함 된 경우 일반 인터페이스 메소드를 처리하는 것과 같은 호출 핸들러 로직을 따르고 대상 객체의 재 작성을 기반으로 메소드 로직을 트리거합니다.
3. 인터페이스에는 중복 메소드 서명이 포함되어 있으며, 이는 인터페이스가 전달되는 순서가 적용됩니다.
읽어 주셔서 감사합니다.이 사이트에 대한 지원에 감사드립니다.