Das Proxy -Modell ist ein häufig verwendetes Java -Designmodell. Seine Eigenschaft ist, dass die Proxy -Klasse und die Delegierteklasse die gleiche Schnittstelle haben. Die Proxy -Klasse ist hauptsächlich für die Vorverarbeitung von Nachrichten, die Filtern von Nachrichten, die Weiterleitungen von Nachrichten an die Delegierklasse und die Verarbeitung von Nachrichten nach dem Ereignis verantwortlich. In der Regel besteht ein Zusammenhang zwischen der Proxy -Klasse und der Delegiertenklasse. Das Objekt einer Proxy -Klasse ist dem Objekt einer Delegiertenklasse zugeordnet. Das Objekt der Proxy -Klasse selbst implementiert den Dienst nicht wirklich, sondern bietet spezifische Dienste, indem sie die relevanten Methoden des Delegiertenklassenobjekts aufrufen.
Vergleich verschiedener dynamischer Proxy -Implementierungen von Java
Schnittstelle
Schnittstelle AddInterface {int add (int a, int b);} Schnittstelle Subinterface {int sub (int a, int b);} Implementierungsklasse
Class Arithmetic implementiert AddInterface, Subinterface {@Override public int sub (int a, int b) {return ab; } @Override public int add (int a, int b) {return a+b; }}Methode 1: Dynamischer Proxy in JDK integriert
1. Implementierungsmethode
Der von Java nach JDK1.3 eingeführte dynamische Proxy -Mechanismus ermöglicht es uns, während der Laufzeit Dynamik Proxy -Klassen zu erstellen. Das Implementieren von AOP mithilfe von Dynamic Proxy erfordert vier Rollen: die Proxy -Klasse, die Proxy -Klasse -Schnittstelle, das Webgerät und den InvocationHandler. Das Webengerät verwendet den Schnittstellenreflexionsmechanismus, um eine Proxy -Klasse zu generieren und dann Code in diese Proxy -Klasse zu weben. Die Proxy -Klasse ist das in AOP erwähnte Ziel. InvocationHandler ist ein Abschnitt, der Ratschläge und Pointcut enthält.
2. Implementierung der VinvocationHandler -Schnittstelle
Klasse jdkdpQueryHandler implementiert InvocationHandler {private arithmetische Real; public jdkdpQueryHandler (arithmetische real) {this.real = real; } @Override öffentliches Objekt invoke (Object Proxy, Methode Methode, Object [] args) löscht Throwable {String methodname = methode.getName (); System.out.println (Methode); System.out.println ("Die Methode:" + methodName + "start, Parameter:" + arrays.aslist (args)); Objektergebnis = methode.invoke (real, args); System.out.println ("Die Methode:"+methodName+"endet, Ergebnis:"+Ergebnis); Rückgabeergebnis; }}3. Erstellen Sie eine Proxy -Klasse und rufen Sie die Proxy -Klasse an
public class main {private static int a = 4, b = 2; öffentliches statisches Objekt createjdkproxy (arithmetische real) {Object proxyarithhmetic = proxy.newproxyInstance (Real.getClass (). getClassloader (), Real.getClass (). getInterfaces (), new jdkdpqueryHandler (real)); Return Proxyarithmus; } public static void main (String [] args) {arithmetic real = new arithmetic (); Object Proxyarithmetic = Createjdkproxy (real); ((AddInterface) Proxyarithhmetik) .Add (a, b); ((Subinterface) Proxyarithhmetik) .Sub (a, b); }}Methode 2: Dynamische Bytecode -Erzeugung (CGGLIB)
1. Implementierungsmethode
Enhancer und MethodInterceptor. Enhancer kann verwendet werden, um eine Klasse dynamisch zu generieren, die eine bestimmte Klasse erben und einige bestimmte Schnittstellen implementieren kann. Gleichzeitig muss Enhancer vor dem Generieren einer Klasse einen Rückruf angeben. Wenn die Klassenmethode aufgerufen wird, wird diesem Rückruf die Ausführung der Methode zugewiesen. MethodInterceptor ist eine weit verbreitete Schnittstelle, die von Callback geerbt wurde, und hat nur eine Methodenerklärung.
2. Vergleich von SchnittstelleninformationenHandler (in JDK) und GrenzflächenmethodeInterceptor (in CGlib)
public interface methodInterceptor erweitert Callback (öffentliche Objekt -Intercept (Object OBJ, java.lang.reflect.method -Methode, Objekt [] args, MethodProxy Proxy). } public interface InvocationHandler {public Object Invoke (Object Proxy, Methode Methode, Object [] args) löst Throwable; } In Bezug auf die Parameterzusammensetzung sind die Eingabeparameter des MethodInterceptor 1 mehr als auf Inchoundhandler. Tatsächlich ist die Bedeutung der ersten drei Parameterobjekte die gleiche wie die von InvocationHandler.
Der erste Parameter gibt an, aus welchem Objekt die Aufrufmethode stammt.
Der zweite Parameter repräsentiert das Methodenobjekt, das die Methode aufruft.
Der dritte Parameter repräsentiert die Liste der Eingabeparameter für diesen Aufruf.
Die zusätzlichen Parameter sind methodProxy -Typ, die ein von CGlib erzeugter Objekt sein sollte, um das Methodenobjekt zu ersetzen. Die Verwendung von MethodProxy verbessert die Effizienz als die direkte Methode der eigenen Methode der JDK direkt.
3.. Real 1
Implementierung der Methodinceptor -Schnittstelle
Klasse cglibDpQueryInterceptor implementiert MethodInterceptor {private arithmetische Real; public cglibdpQueryInterceptor (arithmetische real) {this.real = real; } @Override public Object Intercept (Objektziel, Methode Methode, Objekt [] args, methodProxy proxy) löscht Throwable {String methodname = methode.getName (); System.out.println (Methode); System.out.println ("Die Methode:" + methodName + "start, Parameter:" + arrays.aslist (args)); // Object ergebnis = methode.invoke (real, args); // Beide Methoden können Objektergebnisse = proxy.invoke (real, args) erhalten; System.out.println ("Die Methode:"+ methodName+ "endet, Ergebnis:"+ Ergebnis); Rückgabeergebnis; }}Erstellen Sie eine Proxy -Klasse und rufen Sie eine Proxy -Klasse an
public class main {private static int a = 4, b = 2; öffentliches statisches Objekt createcglibproxy (arithmetische real) {Enhancer Enhancer = new Enhancer (); Enhancer. Enhancer.SetInterfaces (real.getClass (). getInterfaces ()); return Enhancer.create (); } public static void main (String [] args) {arithmetic real = new arithmetic (); Object Proxyarithmetic = createcglibProxy (real); ((AddInterface) Proxyarithhmetik) .Add (a, b); ((Subinterface) Proxyarithhmetik) .Sub (a, b); }}Beachten Sie, dass MethodProxy bei der Ausführung von Funktionen 2 Methoden liefert.
öffentliches Objekt aufrufen (Object OBJ, Object [] args) wirft eintelligbares öffentliches Objekt auf.
Unter ihnen sagt der Javadoc, dass diese Invoke () -Methode für die Ausführung anderer Objekte in derselben Klasse verwendet werden kann, dh der OBJ in dieser Methode muss in ein anderes Objekt derselben Klasse übergeben (die obige Methode besteht darin, in verschiedene Objekte des Arithmetikunterrichts zu gelangen), ansonsten wird nach dem Test in eine unendliche Rekursivsteigung eingetragen). Wenn Sie sorgfältig darüber nachdenken, werden Sie feststellen, dass das Ziel im öffentlichen Objekt -Intercept (Objektziel, Methode, Objekt [] args, methodProxy proxy) ein implementiertes Proxy -Objekt ist. Wenn die Methode add () durch das Ziel aufgerufen wird, wird die Intercept () -Methode ausgelöst, die aufgerufen werden kann. Wenn Method.Invoke (Ziel, Args) in der Methode intercept () aufgerufen wird, ist es gleichwertig, die Methode add () aufzurufen, was zu einer unendlichen rekursiven Schleife führt. Wenn Sie jedoch die Methode ausführen.
Hier ist ein Beispiel zum Simulieren:
Schnittstelle SolVeInterface {void Solve ();} Klasse Real Implements SolveInterface {public void Solve () {system.out.println ("Real Solve!"); }} Klasse -Ziel erweitert real {private Object obj; public void setObject (Objekt obj) {this.obj = obj; } private void invoke () {try {method method = SolvEinterface.class.getMethod ("Solve", neue Klasse [] {}); method.invoke (OBJ, neue Klasse [] {}); } catch (Ausnahme e) {e.printstacktrace (); }} public void Solve () {System.out.println ("Target Solve!"); aufrufen(); }} public class main {public static void main (String [] args) löst eine Ausnahme aus {target target = new target (); target.setObject (neu real ()); // corre // target.setObject (Ziel); // Cyclic Call tritt target.Solve (); }}Tatsächlich ruft die Methode der Methode invoke () die entsprechende Lösung () nach dem OBJ -Typ, dh Polymorphismus, auf.
4. Real 2
Implementierung der Methodinceptor -Schnittstelle
Klasse cGlibDPQueryInterceptor implementiert methodInterceptor {@Override public Object Intercept (Objektziel, Methode, Objekt [] args, MethodProxy -Proxy) löscht Throwable {String methodname = methode.getName (); System.out.println (Methode); System.out.println (Methode); System.out.println ("Die Methode:" + methodName + "start, Parameter:" + arrays.aslist (args)); // Klasseninformationen drucken: target.getClass (); Auslösen von Objekt Ergebnis = Proxy.Invokesuper (Ziel, Args); System.out.println ("Die Methode:"+methodName+"endet, Ergebnis:"+Ergebnis); Rückgabeergebnis; }}Erstellen Sie eine Proxy -Klasse und rufen Sie eine Proxy -Klasse an
public class main {private static int a = 4, b = 2; öffentliches statisches Objekt createcglibproxy () {Enhancer Enhancer = new Enhancer (); Enhancer. Enhancer.SetsuperClass (arithmetic.class); return Enhancer.create (); } public static void main (String [] args) {arithmetic real = new arithmetic (); Object Proxyarithmetic = createcglibProxy (); ((AddInterface) Proxyarithhmetik) .Add (a, b); ((Subinterface) Proxyarithhmetik) .Sub (a, b); }}Beachten Sie, dass Enhancer die Schnittstelle in Implementierung 2 nicht festlegt, da SuperClass festgelegt ist (dh die übergeordnete Klasse der Proxy -Klasse ist Arithmetik), unsere Proxy -Klasse wird sie erben und die Arithmetik hat unsere Schnittstelle implementiert. Um dies zu beweisen, können Sie die Klasseninformationen von target.getClass () in der Intercept -Methode des MethodInterceptor drucken. Sie werden feststellen, dass die übergeordnete Klasse der CGGLIB Proxy -Klasse unterschiedlich ist. wie folgt:
Implementierung 1:
öffentliche Klasse com.test.arithmus $$ EnhancerByCglib $$ 4FA786EB erweitert Java.lang.Object
Implementierung 2:
öffentliche Klasse com.test.arithhmetic $$ EnhancerByCglib $$ 4FA786EB erweitert Com.test.arithmus
Methode 3: Javassist generiert dynamische Proxy (Agent Factory Creation oder dynamische Codeerstellung)
Javassist ist ein Framework zum Bearbeiten von Bytecode, mit dem Sie Bytecode sehr einfach bedienen können. Es kann die Klasse während der Laufzeit definieren oder ändern. Das Prinzip der Implementierung von AOP mit Javassist besteht darin, die Methode, die vor dem Bytecode geladen wird, direkt zu ändern. Dies ist effizienter als die Verwendung von CGLIB zur Implementierung von AOP, und es gibt nicht viele Einschränkungen. Das Implementierungsprinzip lautet wie folgt:
Implementierung 1:
Implementierung von Schnittstellen
Klasse JavassistDPQueryHandler implementiert MethodeHandler {@Override public Object invoke (Objektziel, Methode Methode, Methode Proxy, Object [] args) löscht Throwable {String methodName = methode.getName (); System.out.println (Methode); System.out.println (Methode); System.out.println ("Die Methode:" + methodName + "start, Parameter:" + arrays.aslist (args)); Objektergebnis = proxy.invoke (Ziel, Argumente); System.out.println ("Die Methode:"+methodName+"endet, Ergebnis:"+Ergebnis); Rückgabeergebnis; }}Erstellen Sie eine Proxy -Klasse und rufen Sie eine Proxy -Klasse an
public class main {private static int a = 4, b = 2; öffentliches statisches Objekt createjavassistProxy () löst eine Ausnahme aus {proxyfactory factory = new ProxyFactory (); factory.setsuperClass (arithmetic.class); factory.setHandler (New JavassistDpQueryHandler ()); return factory.createClass (). newInstance (); } public static void main (String [] args) löst eine Ausnahme aus {arithmetic real = new arithmetic (); Object Proxyarithmetic = CreateJavassistProxy (); ((AddInterface) Proxyarithhmetik) .Add (a, b); ((Subinterface) Proxyarithhmetik) .Sub (a, b); }}HINWEIS: Die Definition der Invoke -Methode in der MethodSandler -Schnittstelle lautet wie folgt:
öffentliches Objekt aufrufen (Objektziel, Methodenmethode, Methodenproxy, Objekt [] args)
Die Methode repräsentiert das Method -Objekt, das die Methode aufruft. Proxy ist das Objekt, das die Methode durch Proxy -Klasse generiert und ersetzt. Andernfalls erzeugt die Verwendung von Method.Invoke (Ziel, Args) einen unendlichen Schleifenaufruf.
Implementierung 2:
Der gemeinsame Proxy -Prozess der Verwendung dynamischer Javassisten unterscheidet sich geringfügig von der vorherigen Methode. Im Javassist kann der dynamische Java -Code generiert werden, um Bytecode zu generieren. Auf diese Weise erstellte dynamische Proxy kann sehr flexibel sein und sogar zur Laufzeit Geschäftslogik generieren.
// benutzerdefinierte Interceptor -Schnittstelle InterceptorHandler { /*** Aufrufen der Methode des dynamischen Proxy -Objekts spiegelt diese Methode wider. Sie können der Implementierung dieser Methode AOP-ähnliche Vor- und Nachbearbeitungen hinzufügen. Nur wenn der folgende Code zu dieser Methode hinzugefügt wird. OBJ, Method -Methode, Objekt [] args) throwable; } // Implementierung der Interceptor -Klasse InterceptorHandlerImpl implementiert InterceptorHandler {@Override public Object Invoke (Object OBJ, Methode Methode, Object [] args) löscht Throwable {String methodname = methode.getName (); System.out.println (Methode); System.out.println (Methode); System.out.println ("Die Methode:" + methodName + "start, Parameter:" + arrays.aslist (args)); Objektergebnis = methode.invoke (obj, args); System.out.println ("Die Methode:"+methodName+"endet, Ergebnis:"+Ergebnis); Rückgabeergebnis; }} class MYPROXYIMPL { / ** Klassenname Suffix der dynamischen Proxy -Klasse* / private endgültige statische String proxy_class_name_suffix = "$ myproxy_"; / ** Interceptor -Schnittstelle*/ private endgültige statische String interceptor_handler_interface = "com.test.InterceptorHandler"; / ** Der Klassenname -Index der dynamischen Proxy -Klasse, um die Duplikation von Klassennamen zu verhindern /*** Die dynamische Proxy -Schnittstelle, die dem Benutzer ausgesetzt ist, gibt das dynamische Proxy -Objekt einer bestimmten Schnittstelle zurück. Beachten Sie, dass die Implementierung dieses Proxys mit dem com.cuishen.myaop.InterceptorHandler -Interceptor* verwendet werden muss. Wenn der Benutzer diesen dynamischen Proxy verwenden möchte, muss er zuerst den com.cuishen.myaop.InterceptorHandler Interceptor interceptor Interface implementieren. * @param clasStoproxy String Der Klassenname der Implementierungsklasse der Schnittstelle, die dynamisch proxyiert werden soll. IllegalAccessException * @throws NotFoundException * @throws CannotCompileException * @throws ClassNotFoundException * @see com.cuishen.myAop.InterceptorHandler */ public static Object newProxyInstance(String interfaceClassName, String classToProxy, String interceptorHandlerImplClassName) throws InstantiationException, IllegalAccessException, NotFoundException, kann nicht compileException, classNotFoundException {class interfaceClass = class.forname (interfaceClassName); Class InterceptorHandlerImplClass = class.ForName (InterceptorHandlerImplClassName); return dynamImImplementsInterface (classtoproxy, interfaceClass, interceptorHandlerImplClass); } /** * Dynamische Implementierung der Schnittstelle, die proxyiert werden soll Benutzerbereitete Implementierungsklasse der Interceptor-Schnittstelle * @Return-Objekt gibt das dynamische Proxy-Objekt einer bestimmten Schnittstelle zurück. NotFoundException, kann nicht compileException, InstantiationException, illegalAccessException {classpool cp = classpool.getDefault (); String interfacename = interfaceClass.getName (); // Dynamisch den Klassennamen des Proxy -Klasse -String -String -ProxyclassName = interfacename + proxy_class_name_suffix + proxyclassidex ++ angeben; // Der Paketname der Schnittstelle, die implementiert werden soll + Schnittstellenname String interfacenamepath = interfaceName; Ctclass ctinterface = cp.getCtClass (interfacenamepath); CTCLASS CC = CP.MakeClass (ProxyclassName); CC.AddInterface (CTInterface); Methode [] methodien = interfaceClass.getMethods (); für (int i = 0; i <methods.length; i ++) {method method = methoden [i]; DynamImPlementsMethodsFromTernface (ClasStoproxy, CC, Methode, InterceptorHandlerImplClass, i); } return (Object) cc.toclass (). newInstance (); } /*** Methode bei der dynamischen Implementierung der Methode* @param clasStoproxy String Der Klassenname der Implementierungsklasse der Schnittstelle, die dynamisch proxyiert werden soll, z. InterceptorClass -Klasse Die vom Benutzer bereitgestellte Interceptor -Implementierungsklasse* @param methodIntex int Index der zu implementierenden Methode* @Throws kann nicht kompilexception*/ private statische Void dynamImplementsMethodsFromInterface (String classtoproxy, ctclass -Implementierer, methodtoimPl, class -class -classtoproxy) throws -methodytoimPl, class -unter -ce -classeClasse -Throws, Int -Methode, Throws | Generatemethodcode (ClasStoproxy, methodtoImpl, InterceptorClass, methodIndex); CtMethod cm = ctnewMethod.make (MethodCode, Implementierer); Implementierer.AddMethod (CM); } /*** Dynamisch den Methodenkörper zusammenstellen. Natürlich ist die Methodeimplementierung im Proxy keine einfache Methodenkopie, sondern spiegelt die Invoke -Methode im Interceptor wider und übergibt die empfangenen Parameter* @param clasStoproxy String Der Klassenname der Implementierungsklasse der Grenzfläche zum dynamisch proxyed proxyed, z. @Param InterceptorClass-Klasse Die von Benutzer bereitgestellte Interceptor-Implementierungsklasse* @param methodIndex int Index der zu implementierten Methode* @return String Die Zeichenfolge der dynamischen Assembly-Methode*/ private statische String-Generatemethodcode (String classtoproxy, Methode-methode-methode-methode-methode-methode-methode-methode-methode (). String methodReturnType = methodeToImpl.getReturnType (). GetName (); Class [] parameter = methodtoImpl.getParameterTypes (); Class [] exceptionTypes = methodeToImpl.getExceptionTypes (); StringBuffer ExceptionBuffer = new StringBuffer (); // Ausnahmegerklärung der Assemblermethode if (exceptionTypes.length> 0) ExceptionBuffer.Append ("Auswürfe"); für (int i = 0; i <exceptionTypes.length; i ++) {if (i! = exceptionTypes.length - 1) ExceptionBuffer.Append (ExceptionTypes [i] .getName ()). append (","); sonst exceptionBuffer.Append (ExceptionTypes [i] .getName ()); } StringBuffer parameterBuffer = new StringBuffer (); // Parameterliste der Assemblermethode für (int i = 0; i <parameters.length; i ++) {Klasse parameter = parameter [i]; String parameterType = parameter.getName (); // Dynamisches Angeben des Variablennamens der Methode Parameter String refname = "a" + i; if (i! = parameters.length - 1) parameterBuffer.Append (parameterType) .Append (" + refname) .Append (", "); sonst parameterBuffer.Append (parameterType) .Append (" + Refname); } StringBuffer methoddeclare = new StringBuffer (); // Methodenerklärung, da es sich um eine Methode handelt, die die Schnittstelle implementiert, handelt es sich um eine öffentliche Methode methoddeclare.append ("public") .Append (methodReturnType) .Append (") .Append (methodName) .Append (" ("). Append (ParameterBuffer) .And (")). Anhang append (AUSSCHLIESSE (AUSGABE (AUSBAR). String interceptorImplname = interceptorClass.getName (); // methodBody methoddeclare.append (interceptor_handler_interface) .Append ("interceptor = new") .Append (interceptorImplName) .Append ("();/n"); // Reflection Call User Interceptor Interface Methoddeclare.Append ("Objekt returnObj = interceptor.invoke (class.forname (/" + classtoproxy + "/"). if (Parameter.Length> 0) MethodeDeclare.Append ("New Object [] {"); methodDeclare.Append ("($ w)" + i + ", sonst methodDeclare.Append (" ($ W) a " + i); sonst methoddeclare.append ("null);/n"); // Wickeln Sie den Rückgabewert des Aufruf -Interceptor if (methodeToImpl.getReturnType (). IsPrimitive ()) {if (methodeToImpl.GetReturnType (). Equals (boolean.type)) methoddeclare.Append ("return ((boolean) returnobj) .BOOLEANVAL ();/n"); sonst if (methodeToImpl.getReturnType (). Equals (Integer.Type)) methoddeclare.Append ("return ((Integer) returnObj) .IntValue ();/n"); sonst if (methodtoImpl.getReturnType (). Equals (long.type) methoddeclare.Append ("return ((long) returnObj) .LongValue ();/n"); sonst if (methodtoImpl.getReturnType (). Equals (float.Type) methoddeclare.Append ("return ((float) returnObj) .floatValue ();/n"); sonst if (methodtoImpl.getReturnType (). Equals (double.Type) methoddeclare.append ("return (" return ("return (" return ("return ();/n"); else if (methodeToImpl.getReturnType (). Equals (double.type)) methodDeclare.append("return("return("return("return("return("return();/n"); else if(methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return("return("return("return("return("return("return("methodToImpl.getReturnType().equals(Double.TYPE)) methoddeclare.append ("return (" return ("return (" return ("return (" methodtoImpl.getReturnType (). Equals (double.Type) methodeDeclare.Append ("return (" return ("return (" return ("return (" methodetoImpl.getRetRetRetType (). methoddeclare.append ("return (" return ("return (" return ("methodtoImpl.getReturnType (). Equal ((doppelte) returnObj) .DoubleValue ();/n"); sonst if (methodeToImpl.getReturnTurntype). . methodDeclare.Append ("return ((byte) returnObj) .ByteValue ();/n"); methodReturntype + ") returnObj;/n"); Klasse, der Klassenname der Proxy-Klasse ist erforderlich, der benutzerdefinierte Interceptor-Implementierungsklassenname Object Proxyarithmetic b);Drucken Sie den Code für die dynamische Implementierung der Schnittstelle wie folgt aus:
public int add (int a0, int a1) {com.test.interceptorHandler interceptor = new com.test.InterceptorHandlerImpl (); Object [] {($ w) a0, ($ w) a1}); return ((Integer) returnObj) .IntValue ();} public int sub (int a0, int a1) {com.Test.InterceptorHandler interceptor = new com.test.InterceTorHandlerimpl (); interceptor.invoke (class.forname ("com.test.arithhmetic"). newInstance (), class.forname ("com.test.arithmetic"). Das obige ist eine detaillierte Einführung in den Dynamic Proxy -Implementierungsmechanismus in Java, und ich hoffe, dass es für das Lernen aller hilfreich sein wird.