프록시 모델은 일반적으로 사용되는 Java 디자인 모델입니다. 그 특성은 프록시 클래스와 대의원 클래스가 동일한 인터페이스를 가지고 있다는 것입니다. 프록시 클래스는 주로 전처리 메시지, 메시지 필터링, 대의원 클래스로 메시지 전달 및 이벤트 후 메시지를 처리 할 책임이 있습니다. 프록시 클래스와 대의원 클래스 사이에는 일반적으로 연관성이 있습니다. 프록시 클래스의 객체는 대의원 클래스의 객체와 관련이 있습니다. 프록시 클래스 자체의 객체는 실제로 서비스를 구현하지는 않지만 대의원 클래스 객체의 관련 방법을 호출하여 특정 서비스를 제공합니다.
Java의 다양한 동적 프록시 구현 비교
인터페이스
인터페이스 addInterface {int add (int a, int b);} 인터페이스 서브 인터페이스 {int sub (int a, int b);} 구현 클래스
클래스 산술 구현 AddInterface, subinterface {@override public int sub (int a, int b) {return ab; } @override public int add (int a, int b) {return a+b; }}방법 1 : JDK에 내장 된 동적 프록시
1. 구현 방법
JDK1.3 이후 Java가 도입 한 동적 프록시 메커니즘을 통해 런타임 중에 프록시 클래스를 동적으로 생성 할 수 있습니다. 동적 프록시를 사용하여 AOP를 구현하려면 프록시 클래스, 프록시 클래스 인터페이스, 직조 장치 및 호출 핸들러의 네 가지 역할이 필요합니다. 직조 장치는 인터페이스 반사 메커니즘을 사용하여 프록시 클래스를 생성 한 다음 코드를이 프록시 클래스로 직조합니다. 프록시 클래스는 AOP에 언급 된 대상입니다. InvocationHandler는 조언과 포인트 컷이 포함 된 섹션입니다.
2. VinVocationHandler 인터페이스 구현
클래스 jdkdpqueryHandler는 invocationHandler {private arithmetic real; public jdkdpqueryHandler (arithmetic real) {this.real = real; } @override public Object invoke (오브젝트 프록시, 메소드 메소드, 개체 [] args) 던져 버릴 수있는 {String methodName = method.getName (); System.out.println (메소드); System.out.println ( "메소드 :" + MethodName + "시작, 매개 변수 :" + arrays.aslist (args)); 객체 결과 = method.invoke (real, args); System.out.println ( "메소드 :"+MethodName+"ENDS, result :"+result); 반환 결과; }}3. 프록시 클래스를 만들고 프록시 클래스를 호출하십시오.
공개 클래스 메인 {private static int a = 4, b = 2; public static 객체 createjdkproxy (arithmetic real) {Object proxyarithmetic = proxy.newproxyInstance (real.getClass (). getClassLoader (), real.getClass (). 근위부 리턴; } public static void main (String [] args) {arithmetic real = new Arithmetic (); Object proxyarithmetic = createjdkproxy (real); ((addinterface) 근위) .add (a, b); ((하위 인터페이스) 근위) .sub (a, b); }}방법 2 : 동적 바이트 코드 생성 (CGLIB)
1. 구현 방법
인핸서 및 메소드 인터셉터. 인핸서를 사용하여 클래스를 동적으로 생성하는 데 사용될 수 있으며,이 클래스는 지정된 클래스를 상속하고 특정 인터페이스를 구현할 수 있습니다. 동시에 인핸서는 클래스를 생성하기 전에 콜백을 지정해야합니다. 클래스 메소드가 호출되면이 콜백에 메소드 실행이 할당됩니다. MethodInterceptor는 콜백에서 상속 된보다 널리 사용되는 인터페이스이며 하나의 메소드 선언 만 있습니다.
2. 인터페이스 invocationHandler (JDK) 및 인터페이스 메소드 인 트레이너 (CGLIB) 비교
public Interface Methodinterceptor 확장 콜백 {public object intercept (Object obj, java.lang.reflect.method method, object [] args, methodproxy proxy) 던지기 가능; } public interface invocationHandler {public Object Invoke (개체 프록시, 메소드 메소드, 개체 [] args) 던지기 가능; } 매개 변수 구성 측면에서, 메소드 인턴 셉터의 입력 매개 변수는 invocationHandler보다 1이 더 높습니다. 실제로, 처음 세 매개 변수 객체의 의미는 InvocationHandler의 의미와 동일합니다.
첫 번째 매개 변수는 호출 메소드가 어떤 객체에서 오는지를 나타냅니다.
두 번째 매개 변수는 메소드를 호출하는 메소드 객체를 나타냅니다.
세 번째 매개 변수는이 호출의 입력 매개 변수 목록을 나타냅니다.
추가 매개 변수는 메소드 프록시 유형이며, 이는 메소드 객체를 대체하기 위해 cglib에 의해 생성 된 객체이어야합니다. MethodProxy를 사용하면 JDK의 자체 방법 자체 메소드를 직접 호출하는 것보다 효율성이 향상됩니다.
3. 실현 1
MethodInterceptor 인터페이스 구현
클래스 cglibdpqueryinterceptor implements methodinterceptor {private arithmetic real; public cglibdpqueryinterceptor (arithmetic real) {this.real = real; } @override public Object Intercept (객체 대상, 메소드 메소드, 개체 [] args, methodProxy proxy) 던질 가능 {String MethodName = method.getName (); System.out.println (메소드); System.out.println ( "메소드 :" + MethodName + "시작, 매개 변수 :" + arrays.aslist (args)); // 객체 결과 = method.invoke (real, args); // 두 방법 모두 객체 result = proxy.invoke (real, args)를 얻을 수 있습니다. System.out.println ( "메소드 :"+ MethodName+ "ENDS, result :"+ result); 반환 결과; }}프록시 클래스를 만들고 프록시 클래스를 호출하십시오
공개 클래스 메인 {private static int a = 4, b = 2; public static 객체 CreateCglibproxy (Arithmetic Real) {Enhancer Enhancer = new Enhancer (); Enhancer.setCallback (New CglibdpQueryInterceptor (Real)); Enhancer.setInterfaces (real.getClass (). getInterfaces ()); return enhancer.create (); } public static void main (String [] args) {arithmetic real = new Arithmetic (); Object proxyarithmetic = CreateCglibproxy (Real); ((addinterface) 근위) .add (a, b); ((하위 인터페이스) 근위) .sub (a, b); }}MethodProxy는 함수를 실행할 때 2 가지 방법을 제공합니다.
공개 물체 호출 (Object obj, Object [] args) 던지기 가능한 공개 물체 호출자 (Object obj, Object [] args) 던지기
그 중에서 Javadoc 은이 invoke () 메소드가 같은 클래스에서 다른 객체의 실행에 사용될 수 있다고 말합니다. 신중하게 생각하면 공개 객체 인터셉트 (객체 대상, 메소드 메소드, Object [] args, methodproxy proxy)의 대상이 구현 된 프록시 객체임을 알 수 있습니다. add () 메소드가 대상을 통해 호출되면 intercept () 메소드가 호출되도록 트리거됩니다. intercept () 메소드에서 method.invoke (target, args)가 호출되면 add () 메소드를 다시 호출하는 것과 같습니다. 그러나 method.invoke (real, args)를 실행한다면, 실제와 대상은 같은 클래스에서 다른 객체이기 때문에 실제는 실제 논리적 주제이며 Target은 실제 주제의 대리입니다.
다음은 시뮬레이션하는 예입니다.
인터페이스 solveInterface {void solve ();} class real implements solveInterface {public void solve () {system.out.println ( "Real Solve!"); }} 클래스 대상은 실제 {private object obj; public void setObject (object obj) {this.obj = obj; } private void invoke () {try {method method = solveInterface.class.getMethod ( "solve", new class [] {}); method.invoke (obj, new class [] {}); } catch (예외 e) {e.printstacktrace (); }} public void solve () {system.out.println ( "대상 해결!"); 부르다(); }} public class main {public static void main (String [] args)은 예외 {target target = new Target (); target.setObject (new real ()); // 정확한 // target.setObject (target); // cyclic 호출이 대상이 발생합니다 .solve (); }}실제로, 메소드의 invoke () 메소드는 OBJ 유형, 즉 다형성에 따라 해당 solve () 메소드를 호출합니다.
4. 실현 2
MethodInterceptor 인터페이스 구현
클래스 cglibdpqueryinterceptor implements methodinterceptor {@override public object intercept (객체 대상, 메소드 메소드, 객체 [] args, methodproxy proxy) 던지기 가능 {String methodName = method.getName (); System.out.println (메소드); System.out.println (메소드); System.out.println ( "메소드 :" + MethodName + "시작, 매개 변수 :" + arrays.aslist (args)); // 클래스 정보 인쇄 : target.getClass (); 객체 result = proxy.invokesuper (target, args); System.out.println ( "메소드 :"+MethodName+"ENDS, result :"+result); 반환 결과; }}프록시 클래스를 만들고 프록시 클래스를 호출하십시오
public class main {private static int a = 4, b = 2; public static 객체 CreateCglibproxy () {Enhancer Enhancer = new Enhancer (); Enhancer.setCallback (New CglibdpQueryInterceptor ()); Enhancer.setSuperClass (arithmetic.class); return enhancer.create (); } public static void main (String [] args) {arithmetic real = new Arithmetic (); Object proxyarithmetic = createcglibproxy (); ((addinterface) 근위) .add (a, b); ((하위 인터페이스) 근위) .sub (a, b); }}슈퍼 클래스가 설정되어 있기 때문에 Enhancer는 구현 2에서 인터페이스를 설정하지 않습니다 (즉, 프록시 클래스의 부모 클래스는 산술이기 때문에 프록시 클래스가 상속되고 산술이 인터페이스를 구현했습니다. 이를 증명하기 위해 Methodinterceptor의 인터셉트 메소드에서 Target.getClass ()의 클래스 정보를 인쇄 할 수 있습니다. CGLIB 프록시 클래스의 상위 클래스가 다르다는 것을 알 수 있습니다. 다음과 같이 :
구현 1 :
공개 클래스 com.test.arithmetic $$ EnhancerBycglib $$ 4fa786eb는 java.lang.object를 확장합니다
구현 2 :
공개 클래스 com.test.arithmetic $$ EnhancerBycglib $$ 4fa786eb는 com.test.arithmetic을 확장합니다
방법 3 : Javassist는 동적 프록시를 생성합니다 (에이전트 공장 생성 또는 동적 코드 생성)
Javassist는 바이트 코드를 편집하기위한 프레임 워크로, 바이트 코드를 매우 간단하게 작동시킬 수 있습니다. 런타임 동안 클래스를 정의하거나 수정할 수 있습니다. Javassist를 사용하여 AOP를 구현하는 원리는 바이트 코드가로드되기 전에 절단 해야하는 방법을 직접 수정하는 것입니다. 이것은 AOP를 구현하기 위해 CGLIB를 사용하는 것보다 더 효율적이며 많은 제한 사항이 없습니다. 구현 원칙은 다음과 같습니다.
구현 1 :
인터페이스 구현
클래스 javassistdpqueryHandler는 메소드 핸들러를 구현합니다. {@Override public Object invoke (개체 대상, 메소드 메소드, 메소드 프록시, 개체 [] args) 던질 가능 {String MethodName = method.getName (); System.out.println (메소드); System.out.println (메소드); System.out.println ( "메소드 :" + MethodName + "시작, 매개 변수 :" + arrays.aslist (args)); 객체 결과 = proxy.invoke (대상, args); System.out.println ( "메소드 :"+MethodName+"ENDS, result :"+result); 반환 결과; }}프록시 클래스를 만들고 프록시 클래스를 호출하십시오
public class main {private static int a = 4, b = 2; public static 객체 CreateJavassistProxy () 예외 {proxyfactory factory = new proxyFactory (); Factory.SetSuperClass (arithmetic.class); Factory.sethandler (New JavassistDPQueryHandler ()); return factory.createclass (). NewInstance (); } public static void main (string [] args)은 예외 {arithmetic real = new Arithmetic (); Object proxyArithmetic = createJavassistProxy (); ((addinterface) 근위) .add (a, b); ((하위 인터페이스) 근위) .sub (a, b); }}참고 : 메소드 핸들러 인터페이스에서 호출 메소드의 정의는 다음과 같습니다.
공개 객체 호출 (객체 대상, 메소드 메소드, 메소드 프록시, 개체 [] args)
메소드는 메소드를 호출하는 메소드 객체를 나타내고 프록시는 메소드를 프록시 클래스로 생성하고 대체하는 객체입니다. 그렇지 않으면 method.invoke (target, args)를 사용하면 무한 루프 호출이 생성됩니다.
구현 2 :
동적 javassist를 사용하는 일반적인 프록시 프로세스는 이전 방법과 약간 다릅니다. Javassist 내부에서는 바이트 코드를 생성하기 위해 동적 Java 코드를 생성 할 수 있습니다. 이러한 방식으로 생성 된 동적 프록시는 매우 유연하며 런타임에 비즈니스 로직을 생성 할 수도 있습니다.
// 사용자 정의 인터셉터 인터페이스 인터셉터 핸들러 { /*** 동적 프록시 객체의 메소드를 호출하면이 메소드가 반영됩니다. 이 방법의 구현에 AOP와 유사한 사전 및 사후 수술을 추가 할 수 있습니다. 다음 코드 가이 메소드에 추가 된 경우에만 프록시 메소드가 실행되고 리턴 값이 프록시로 반환되고 최종적으로 프로그램으로 반환됩니다.* @param obj는 프록시 객체* @param 메소드 프록시 객체* @Param Args Object [] 프록시 객체의 매개 변수*/ reture value of the Object 's Throws Throws thething*/ @Throw value의 return value the Propubly Object* @Param Args Object [] obj, method method, object [] args) 던지기 가능; } // 인터셉터 클래스의 구현 interceptorHandlerImpl은 인터셉터 핸드러 (@Override public Object invoke) (개체 obj, 메소드 메소드, 오브젝트 [] args) 던지기 가능 {String methodName = method.getName (); System.out.println (메소드); System.out.println (메소드); System.out.println ( "메소드 :" + MethodName + "시작, 매개 변수 :" + arrays.aslist (args)); 객체 결과 = method.invoke (obj, args); System.out.println ( "메소드 :"+MethodName+"ENDS, result :"+result); 반환 결과; }} class myproxyimpl { / ** 클래스 이름 접미사 동적 프록시 클래스* / 개인 최종 정적 문자열 proxy_class_name_suffix = "$ myProxy_"; / ** 인터셉터 인터페이스*/ 개인 최종 정적 문자열 interceptor_handler_interface = "com.test.interceptorHandler"; / ** 클래스 이름의 복제를 방지하기위한 동적 프록시 클래스의 클래스 이름 색인*/ private static int proxyclassIndex = 1; /*** 사용자에게 노출 된 동적 프록시 인터페이스는 특정 인터페이스의 동적 프록시 객체를 반환합니다. 이 프록시의 구현은 com.cuishen.myaop.interceptorhandler interceptor*와 함께 사용해야합니다. 즉, 사용자 가이 동적 프록시를 사용하려면 먼저 com.cuishen.myaop.interceptorhandler interceptorceptorface* interfaceclassname string을 동적 인 proxy, eg test. @Param ClasStoProxy 문자열 인터페이스의 구현 클래스의 클래스 이름은 동적으로 프록시로 표시됩니다. delegalaccessexception * @throws notfoundexception * @throws conclockilexception * @throws classNotFoundException * @see com.cuishen.myaop.interceptorHandler */ public static 객체 NewProxyInstance (string interfaceclassName, String interclassOnceme) 불법 행위 exception, notFoundException, canclecompileException, classNotFoundException {class interfaceclass = class.forname (interfaceclassName); 클래스 interceptorHandlerImplClass = class.forname (interceptorHandlerImplClassName); return dynamicimplesmentmentsInterface (ClasstoProxy, interfaceclass, interceptorHandlerImplClass); } /** * 프록시 할 인터페이스의 동적 구현 * @param classtoproxy 문자열 인터페이스의 클래스 이름은 동적으로 프록시로, 예를 들어, test.studentInfoserviceimpl * @param interfaceclass classface를 동시기적으로 proxyed, eg testinfostor be @paramporith intervice를 위해 classeclass intervice intervice intervice intervice intervice intervice intervice interversepresse 인터셉터 인터페이스의 사용자 제공 구현 클래스 * @return 객체 특정 인터페이스의 동적 프록시 객체를 반환합니다 * @throws notfoundException * @throws cancompileException * @throws InstantiationException * @throws 불법 정적 객체 동적 상상 */ private static 객체 동적화 (string classtoproxy, classe interfaceclash) NotFoundException, concectilexception, InstantiationException, OrengalAccessException {classpool cp = classpool.getDefault (); 문자열 interfaceName = interfaceclass.getName (); // 프록시 클래스 문자열의 클래스 이름을 동적으로 지정합니다. // 구현할 인터페이스의 패키지 이름 + 인터페이스 이름 string interfacenamepath = interfaceName; ctclass ctinterface = cp.getctclass (interfacenamepath); ctclass cc = cp.makeclass (proxyClassName); cc.addinterface (ctinterface); 메소드 [] 메소드 = interfaceclass.getMethods (); for (int i = 0; i <methods.length; i ++) {method method = method [i]; DynamicimplesmentsMethodsfrominterface (Classtoproxy, CC, Method, InterceptorHandlerImplClass, i); } return (Object) cc.toclass (). newInstance (); } /*** 메소드의 동적 구현에서 메소드* @param classtoproxy 문자열 인터페이스의 구현 클래스의 클래스 이름을 동적으로 프록시 할 수 있습니다. InterceptorClass 클래스 사용자가 제공하는 인터셉터 구현 클래스* @Param MethodIngex int 구현 방법의 @param methodIngex int 지수* @throws cancle compileException*/ private static void dynamicimmentsmethodsfrominterface (String ClasStoproxy, method toimpl, class interceptorclass, int methodNexemence) {string interceptorclass, ctclass interceptorclass. GeneratemEthodCode (ClasStoProxy, MethodToImpl, interceptorClass, MethodIndex); ctmethod cm = ctnewmethod.make (MethodCode, 구현 자); 구현 자 .addmethod (cm); } /*** 메소드 본문을 동적으로 조립합니다. 물론, 프록시의 메소드 구현은 간단한 메소드 사본이 아니지만 인터셉터의 호출 메소드를 반영하고 수신 된 매개 변수* @param classtoproxy 문자열을 전달하여 인터페이스의 구현 클래스 이름을 동적으로 프록시로 표시하기 위해 @g test. @param interceptorclass 클래스 사용자가 제공하는 인터셉터 구현 클래스* @param methodIndex int 메소드의 @param methodIndex* @return string 동적 어셈블리 메소드의 문자열*/ private static string generatemethodcode (String classtoproxy, methodtoimpl, class interceptorclass, int methodind) String MethodReTurnType = MethodToImpl.getReturnType (). getName (); class [] parameters = methodtoimpl.getParameterTypes (); class [] ExceptionTypes = methodtoimpl.getexceptionTypes (); StringBuffer ExceptionBuffer = New StringBuffer (); // 어셈블리 메소드의 예외 선언 (ExceptionTypes.length> 0) ExceptionBuffer.Append ( "Throws"); for (int i = 0; i <exceptionTypes.length; i ++) {if (i! = exceptypes.length -1) ExceptionBuffer.append (ExceptionTypes [i] .getName ()). 부록 ( ","); else exceptionbuffer.append (ExceptionTypes [i] .getName ()); } StringBuffer ParameterBuffer = New StringBuffer (); // (int i = 0; i <parameters.length; i ++)에 대한 어셈블리 메소드의 매개 변수 목록 {클래스 매개 변수 = 매개 변수 [i]; 문자열 parametertype = parameter.getName (); // dynamic 메소드 매개 변수 이름을 지정하여 string refname = "a" + i; if (i! = parameters.length -1) ParameterBuffer.append (parameterType) .append ( " + refName) .append (", "); else parameterBuffer.append (parameterType) .append (" + refName); } StringBuffer MethodDeclare = New StringBuffer (); // 메소드 선언은 인터페이스를 구현하는 메소드이므로 public methodDeclare.append ( "public") .append (methodreturnType) .append ( "") .append (methodName) .append ( "("). Append (ParameterBuffer) .append ( ")". String interceptorimplName = interceptorClass.getName (); // MethodBody MethodDeclare.append (interceptor_handler_interface) .append ( "interceptor = new") .append (interceptorimplName) .append ( "();/n"); // 반사 통화 사용자의 인터셉터 인터페이스 인터페이스 MethodDeclare.append ( "Object ReturnObj = interceptor.invoke (/" " + ClasStoProxy +"/"). newInstance (), class.forname (/" " + classtoproxy +"/"). if (parameters.length> 0) MethodDeclare.Append (int i = 0; i <parameters.length; i ++) {// ($ w)는 해당 래퍼 유형으로 변환합니다. + i + ","); else 메소드 reclare.append ( "($ w) a" + i); else MethodDeclare.append ( "null);/n"); // (methodtoimpl.getReturntype (). isprimitive ()) {if (methodtoImpl.getReterTurnType ())) methodDeclare.append ( "return (boolean) returnObj) .BooleanValue ();/n"); else if (methodToImpl.getReTurnType (). Equals (integer.type)) methodDeclare.Append ( "return ((integer) returnObj)) .intValue ();/n"); else if (methodToImpl.getReTurnType (). Equals (long.type)) methodDeclare.Append ( "return ((long) returnObj) .longValue ();/n"); else if (methodToImpl.getReTurnType (). Equals (float.type)) MethodDeclare.append ( "return (float) returnObj) .floatValue ();/n"); else if (methodtoimpl.getReturntype (). equals (double.type)) methodDeclare.append ( "return ("return ( "return ("return ( "return ();/n")); else if (methodToImpl.getReTernType (). equals (double.type)) MethodDecLare.append ( "return ("return ( "return ("return ( "return ("return ( "return ();/n"); else if (methodtoImpl.getReterTurnType ()) methodDecLare.Append ( "return ("return ( "return ("return ( "return ("return ( "return ("return ( "return ("return) MethodDeclare.append ( "return ("return ( "return ("return ( "return ("return ( "return ("methodtoImpl.getReturntype (). equals (double.type))) methodDeclare.append ( "return ("return ( "return ("return ( "return ("return ( "return ("return) MethodDeclare.append ( "return ("return ( "return ("return ( "return ("methodtoImpl.getReTurnType (). equal ((double) returnObj)) ((charac MethodDeclare.append (return (byte) returnObj) .ByteValue ();/n "; MethodErturnType + ") returnObj;/n"; 클래스, 프록시 클래스의 클래스 이름이 필요합니다. 사용자 정의 인터셉터 구현 클래스 클래스 이름 proxyarithmetic = myproxyimpl.newproxyInstance ( "com.test.arithmeticinterface", "com.test.arithmetic", "intersceptorhandlerimpl" b);인터페이스를 동적으로 구현하기위한 코드를 다음과 같이 인쇄하십시오.
public int add (int a0, int a1) {com.test.interceptorhandler interceptor = new com.test.interceptorhandlerimpl (); Object ReturnObj = interceptor.invoke (class.forname ( "com.test.arithmetic"). newInstance (), class.forname ( "newtest.arithmetic"). Object [] {($ w) a0, ($ w) a1}); return ((integer) returnObj) .intValue ();} public int sub (int a0, int a1) {com.test.interceptorhandler interceptor = new com.test.intercrectorHandlerImpl (); ObjectorceporHandlerImpl (); interceptor.invoke (class.forname ( "com.test.arithmetic"). newInstance (), class.forname ( "com.test.arithmetic"). getMethods () [1], new Object [] {($ w) a0, ($ w) a1}); return ((intger) returobj).}. 위의 내용은 Java의 동적 프록시 구현 메커니즘에 대한 자세한 소개이며, 모든 사람의 학습에 도움이되기를 바랍니다.