Le modèle proxy est un modèle de conception Java couramment utilisé. Sa caractéristique est que la classe proxy et la classe de délégués ont la même interface. La classe proxy est principalement responsable de la prétraitement des messages, du filtrage des messages, du transfert des messages à la classe du délégué et du traitement des messages après l'événement. Il existe généralement une association entre la classe proxy et la classe du délégué. L'objet d'une classe de proxy est associé à l'objet d'une classe de délégué. L'objet de la classe proxy lui-même n'implémente pas vraiment le service, mais fournit des services spécifiques en appelant les méthodes pertinentes de l'objet de classe de délégué.
Comparaison de diverses implémentations proxy dynamiques de Java
interface
Interface addInterface {int Add (int a, int b);} Interface subinterface {int sub (int a, int b);} Classe d'implémentation
Class Arithmetic implémente addInterface, subinterface {@Override public int sub (int a, int b) {return ab; } @Override public int Add (int a, int b) {return a + b; }}Méthode 1: Proxy dynamique intégré à JDK
1. Méthode d'implémentation
Le mécanisme proxy dynamique introduit par Java après JDK1.3 nous permet de créer dynamiquement des classes proxy pendant l'exécution. La mise en œuvre de l'AOP à l'aide du proxy dynamique nécessite quatre rôles: la classe de proxy, l'interface de classe proxy, le dispositif de tissage et l'invocationHandler. Le dispositif de tissage utilise le mécanisme de réflexion d'interface pour générer une classe de proxy, puis tisser le code dans cette classe de proxy. La classe proxy est la cible mentionnée dans AOP. InvocationHandler est une section, qui contient des conseils et des points de point.
2. Implémentation de l'interface VinvocationHandler
La classe JDKDPQueryHandler implémente InvocationHandler {private arithmetic réel; public jdkdpqueryhandler (arithmetic réel) {this.real = réel; } @Override Public Object Invoke (Object Proxy, Method Method, Object [] args) lève Throwsable {String MethodName = Method.getName (); System.out.println (méthode); System.out.println ("La méthode:" + méthodyname + "start, paramètres:" + arrays.aslist (args)); Objet résultat = méthode.invoke (réel, args); System.out.println ("La méthode:" + MethodName + "Ends, Résultat:" + Result); Résultat de retour; }}3. Créez une classe proxy et appelez la classe proxy
classe publique Main {private static int a = 4, b = 2; public static objet createjdkproxy (arithmetic réel) {objet proxyarithmetic = proxy.newproxyinstance (real.getClass (). GetClassOader (), real.getClass (). getInterfaces (), new JDKDPQueryHandler (real)); retour proxyarithmétique; } public static void main (string [] args) {arithmetic real = new arithmetic (); Objet proxyarithmetic = createjdkproxy (réel); ((AddInterface) proxyarithmétique) .add (a, b); ((Sous-interface) Proxyarithmétique) .Sub (a, b); }}Méthode 2: Génération dynamique des bytecodes (CGLIB)
1. Méthode d'implémentation
Enhancer et MethodInterceptor. Enhancer peut être utilisé pour générer dynamiquement une classe, qui peut hériter d'une classe spécifiée et implémenter certaines interfaces spécifiées. Dans le même temps, Enhancer doit spécifier un rappel avant de générer une classe. Lorsque la méthode de classe est appelée, l'exécution de la méthode est attribuée à ce rappel. MethodInterceptor est une interface plus largement utilisée héritée à partir du rappel, et il n'a qu'une seule déclaration de méthode.
2. Comparaison de l'interface InvocationHandler (dans JDK) et d'interface MethodInterceptor (dans CGLIB)
Méthode d'interface publique Interceptor étend le callback {public Object Intercept (Object Obj, java.lang.reflect.method Method, Object [] args, méthodyproxy proxy) lance lance-lancenable; } Interface publique InvocationHandler {public Object Invoke (proxy objet, méthode de la méthode, objet [] args) lance Throwsable; } En termes de composition des paramètres, les paramètres d'entrée de MethodInterceptor sont 1 plus que l'invocationHandler. En fait, la signification des trois premiers objets de paramètre est le même que celui de l'invocationHandler.
Le premier paramètre indique de quel objet l'objet provient de l'appel;
Le deuxième paramètre représente l'objet de méthode qui appelle la méthode;
Le troisième paramètre représente la liste des paramètres d'entrée pour cet appel;
Les paramètres supplémentaires sont de type méthodeproxy, qui devraient être un objet généré par CGLIB pour remplacer l'objet de méthode. L'utilisation de MethodProxy améliorera l'efficacité que d'appeler directement la propre méthode de la méthode JDK.
3. Réalisez 1
Implémentation de l'interface MethodInterceptor
Classe CGLIBDPQueryInterceptor implémente MethodInterceptor {private arithmetic réel; public cglibdpqueryInterceptor (arithmetic réel) {this.real = réel; } @Override Public Object Intercept (Object Target, méthode Method, objet [] args, méthodyProxy proxy) lève throwsable {string methethnname = method.getName (); System.out.println (méthode); System.out.println ("La méthode:" + méthodyname + "start, paramètres:" + arrays.aslist (args)); // objet résultat = méthode.invoke (réel, args); // Les deux méthodes peuvent obtenir un résultat objet = proxy.invoke (réel, args); System.out.println ("La méthode:" + MethodName + "Ends, Résultat:" + Result); Résultat de retour; }}Créez une classe proxy et appelez une classe proxy
classe publique Main {private static int a = 4, b = 2; public static objet createcglibproxy (arithmétique réel) {Enhancer Enhancer = new Enhancer (); Enhancer.SetCallback (nouveau CGLIBDPQueryInterceptor (Real)); Enhancer.SetInterfaces (Real.Glass (). GetInterFaces ()); return Enhancer.Create (); } public static void main (string [] args) {arithmetic real = new arithmetic (); Objet proxyarithmetic = createCgliBproxy (réel); ((AddInterface) proxyarithmétique) .add (a, b); ((Sous-interface) Proxyarithmétique) .Sub (a, b); }}Notez que MethodProxy fournit 2 méthodes lors de l'exécution de fonctions.
L'objet public invoque (objet obj, objet [] args) lance un objet public jetable invoquer (objet obj, objet [] args) lance le jetable
Parmi eux, le Javadoc dit que cette méthode invoke () peut être utilisée pour l'exécution d'autres objets dans la même classe, c'est-à-dire que l'OBJ dans cette méthode doit passer dans un autre objet de la même classe (la méthode ci-dessus consiste à passer dans différents objets de la classe arithmétique), sinon il entrera dans une boucle récursive infinie (StackOrtorror apparaît vraiment après le test). Si vous y réfléchissez attentivement, vous constaterez que la cible dans l'interception d'objet public (cible objet, méthode de la méthode, objet [] args, proxy méthodeproxy) est un objet proxy implémenté. Lorsque la méthode Add () est appelée via la cible, la méthode Intercept () sera déclenchée pour être appelée. Si la méthode.invoke (cible, args) est appelée dans la méthode Intercept (), elle équivaut à appeler à nouveau la méthode Add (), résultant en une boucle récursive infinie. Mais si vous exécutez Method.Invoke (réel, args), cela ne sera pas réel et cible sont différents objets dans la même classe, le vrai est le sujet logique réel, et la cible est le proxy pour le sujet réel réel.
Voici un exemple pour simuler:
Interface SolveInterface {void Solve ();} class Real implémente SolveInterface {public void Solve () {System.out.println ("Real Solve!"); }} La cible de classe étend Real {objet privé obj; public void setObject (objet obj) {this.obj = obj; } private void invoke () {try {méthode méthode = solveinterface.class.getMethod ("solve", new class [] {}); méthode.invoke (obj, new class [] {}); } catch (exception e) {e.printStackTrace (); }} public void Solve () {System.out.println ("Target Solve!"); invoquer(); }} classe publique Main {public static void main (String [] args) lève l'exception {cible cible = new Target (); Target.SetObject (new Real ()); // correct // Target.SetObject (Target); // L'appel cyclique se produit Target.Solve (); }}En fait, la méthode invoke () de la méthode appellera la méthode Solve () correspondante en fonction du type OBJ, c'est-à-dire le polymorphisme.
4. Réalisez 2
Implémentation de l'interface MethodInterceptor
Class CGLIBDPQueryInterceptor implémente MethodInterceptor {@Override Public Object Intercept (Object Target, Method Method, Object [] args, méthodyProxy proxy) lève le throwable {String MethodName = method.getName (); System.out.println (méthode); System.out.println (méthode); System.out.println ("La méthode:" + méthodyname + "start, paramètres:" + arrays.aslist (args)); // Imprimez les informations de la classe: Target.getClass (); omettre objet résultat = proxy.invokesuper (cible, args); System.out.println ("La méthode:" + MethodName + "Ends, Résultat:" + Result); Résultat de retour; }}Créez une classe proxy et appelez une classe proxy
classe publique Main {private static int a = 4, b = 2; public static objet 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 (); Objet proxyarithmetic = createCgliBproxy (); ((AddInterface) proxyarithmétique) .add (a, b); ((Sous-interface) Proxyarithmétique) .Sub (a, b); }}Notez que Enhancer ne définit pas l'interface dans l'implémentation 2, car Superclass est défini (c'est-à-dire que la classe parent de la classe de proxy est arithmétique), notre classe de proxy héritera et arithmétique a implémenté notre interface. Pour le prouver, vous pouvez imprimer les informations de classe de Target.getClass () dans la méthode Intercept de MethodInterceptor. Vous constaterez que la classe parent de la classe proxy CGLIB est différente. comme suit:
Implémentation 1:
classe publique com.test.arithmetic $$ EnhancerByCglib $$ 4FA786EB étend java.lang.object
Implémentation 2:
classe publique com.test.arithmetic $$ EnhancerByCglib $$ 4fa786eb étend com.test.arithmetic
Méthode 3: Javassist génère un proxy dynamique (création d'usine d'agent ou création de code dynamique)
Javassist est un framework pour modifier ByteCode, qui vous permet de faire fonctionner très simplement ByteCode. Il peut définir ou modifier la classe pendant l'exécution. Le principe d'implémentation AOP à l'aide de Javassist est de modifier directement la méthode qui doit être saisie avant le chargement du bytecode. Ceci est plus efficace que l'utilisation de CGLIB pour implémenter AOP, et il n'y a pas beaucoup de restrictions. Le principe de mise en œuvre est le suivant:
Implémentation 1:
Implémentation des interfaces
class javassistdpqueryhandler implémente méthodehandler {@Override public objet invoke (objet cible, méthode méthode, proxy de méthode, objet [] args) lance throwable {string methodnname = méthode.getName (); System.out.println (méthode); System.out.println (méthode); System.out.println ("La méthode:" + méthodyname + "start, paramètres:" + arrays.aslist (args)); Objet résultat = proxy.invoke (cible, args); System.out.println ("La méthode:" + MethodName + "Ends, Résultat:" + Result); Résultat de retour; }}Créez une classe proxy et appelez une classe proxy
classe publique Main {private static int a = 4, b = 2; objet statique public createjavassistProxy () lève une exception {proxyfactory factory = new proxyfactory (); factory.setsuperclass (arithmetic.class); factory.sethandler (new JavassistDpQueryHandler ()); return factory.CreateClass (). NewInstance (); } public static void main (String [] args) lève l'exception {arithmetic real = new arithmetic (); Objet proxyarithmetic = createjavassistProxy (); ((AddInterface) proxyarithmétique) .add (a, b); ((Sous-interface) Proxyarithmétique) .Sub (a, b); }}Remarque: La définition de la méthode invoquée dans l'interface MethodHandler est la suivante:
Invoque d'objet public (cible objet, méthode de la méthode, proxy de méthode, objet [] args)
La méthode représente l'objet de méthode qui appelle la méthode, le proxy est l'objet qui génère et remplace la méthode par classe proxy. Sinon, l'utilisation de méthode.invoke (cible, args) générera un appel de boucle infinie.
Implémentation 2:
Le processus proxy commun de l'utilisation du javassiste dynamique est légèrement différent de la méthode précédente. À l'intérieur de Javassist, le code Java dynamique peut être généré pour générer du bytecode. Le proxy dynamique créé de cette manière peut être très flexible et peut même générer une logique commerciale au moment de l'exécution.
// interface interceptor personnalisée InterceptorHandler {/ ** * Appeler la méthode d'objet proxy dynamique reflétera cette méthode. Vous pouvez ajouter des pré- et post-opérations de type AOP à la mise en œuvre de cette méthode. Seulement si le code suivant est ajouté à ce corps de méthode * La méthode proxy sera exécutée et que la valeur de retour sera renvoyée au proxy et finalement renvoyée au programme * @param obj objet l'objet proxy * @param Méthode Méthode L'objet proxy * @param args Object [] Paramètres de l'objet proxy * @rerurn Object Value Invoid of the proxy objet de l'objet Proxy * @throws throws OBJ, méthode de la méthode, objet [] args) lance Throwsable; } // Implémentation de la classe interceptor interceptorhandlerImpl implémente interceptorhandler {@Override public objet invoke (objet obj, méthode méthode, objet [] args) lève lanceable {string methodname = method.getName (); System.out.println (méthode); System.out.println (méthode); System.out.println ("La méthode:" + méthodyname + "start, paramètres:" + arrays.aslist (args)); Résultat objet = méthode.invoke (obj, args); System.out.println ("La méthode:" + MethodName + "Ends, Résultat:" + Result); Résultat de retour; }} classe myProxyImpl {/ ** Nom de classe suffixe de la classe proxy dynamique * / private final static String proxy_class_name_suffix = "$ myproxy_"; / ** Interceptor Interface * / private final static String interceptor_handler_interface = "com.test.interceptorhandler"; / ** L'indice de nom de classe de la classe de proxy dynamique pour empêcher la duplication des noms de classe * / private static int proxylassIndex = 1; / ** * L'interface proxy dynamique exposée à l'utilisateur, renvoie l'objet proxy dynamique d'une certaine interface. Notez que la mise en œuvre de ce proxy doit être utilisée avec l'intercepteur com.cisen.myaop.InterceptorHandler *, c'est-à-dire que si l'utilisateur veut utiliser ce proxy dynamique, il doit d'abord implémenter l'interface interfaceclasson de l'interceptor @param classtoproxy String Le nom de classe de la classe d'implémentation de l'interface à proxy dynamiquement, par exemple test.StudentinFoserviceIMPl * @param interceptorhandlerImpllSclassName String Le nom de classe de la classe d'implémentation de l'interface interceptor fournie par l'utilisateur * @return objet renvoie l'objet de proxy dynamique d'une certaine interface * @throws instantialexception * IllégalAccessException * @Throws NotfoundException * @throws notgCompileException * @throws classNotFoundException * @see com.cincen.myaop.InterceptorHandler * / public static objet NewProxyInstance (String InterfaceClassName, String Classtoproxy, interceptorpor-gandlerimplla IllégalAccessException, NotFoundException, NodCompileException, classNotFoundException {class interfaceClass = class.forname (interfaceClassName); Classe interceptorhandlerImpLlass = class.forname (interceptorhandlerImpLclassName); return dynamicIMPlementsInterface (classtoproxy, interfaceClass, interceptorhandlerImpLlass); } / ** * Implémentation dynamique de l'interface à proxy * @param classtoproxy String Le nom de classe de la classe d'implémentation de l'interface à proxyation dynamiquement, par exemple la classe interface de la classe StudentinFoserviceImp * @param interfacesevice * @Param Class. Classe d'implémentation fournie par l'utilisateur de l'interface interceptor * @return objet renvoie l'objet proxy dynamique d'une certaine interface * @throws notfoundException * @throws nodCompileException * @Throws InstantiationException * @throws illégalaccessException * / private static static dynamicilPlementsInterface (String classtoproxy, class interfaceclash NotFoundException, NodCompileException, InstantiationException, illégalAccessException {ClassPool CP = CLASSPOOL.GetDefault (); String interfacename = interfaceClass.getName (); // Spécifiez dynamiquement le nom de classe de la chaîne de classe proxy proxyclassName = interfacename + proxy_class_name_suffix + proxyclassIndex ++; // Le nom du package de l'interface à implémenter + la chaîne de nom d'interface interfacenamepath = interfacename; CtClass ctinterface = cp.getctClass (interfacenamepath); CTClass CC = CP.MakeClass (proxyClassName); CC.AddInterface (CTinterface); Méthode [] méthodes = interfaceClass.getMethods (); for (int i = 0; i <méthodes.length; i ++) {méthode méthode = méthodes [i]; DynamicImplementsMethodsFromInterface (classtoproxy, cc, méthode, interceptorhandlerImpLlass, i); } return (objet) cc.toclass (). newInstance (); } / ** * Méthode dans l'implémentation dynamique de la méthode * @param classtoproxy String Le nom de classe de la classe d'implémentation de l'interface à proxyer dynamiquement, par exemple test.StudentinFoserviceImpl * @param implémentateur ctclass l'emballage de la classe Dynamic Proxy * @param méthode de la méthode @paramplage de la méthode d'interface pour être implémentée dans la dynamique proxy CASSE @paraml Classe interceptorclass La classe d'implémentation interceptrice fournie par l'utilisateur * @param méthode index int Int de la méthode à implémenter * @throws notmompileException * / private static void dynamicimplementsMethodsfromInterface (String cllasstoproxy, ctclass impilter, méthode méthodetoImpl, class interceptorclass, int méthodindex) throw GenerateMethodcode (classtoproxy, méthodetoimpl, interceptorclass, methodindex); CtMethod cm = ctNewMethod.Make (méthodecode, implémentateur); EMPLETEMER.ADDMETHOD (CM); } / ** * Assemblez dynamiquement le corps de la méthode. Bien sûr, l'implémentation de la méthode dans le proxy n'est pas une copie de méthode simple, mais reflète la méthode invoquée dans l'intercepteur et transmet les paramètres reçus * @param classtoproxy string le nom de classe de la classe d'implémentation de l'interface pour être proxyé dynamiquement, par exemple, test.StudentInFoserviceImpl * @Param MethodToImpl @param interceptorclass classe la classe d'implémentation interceptor fournie par l'utilisateur * @param méthodyIndex intde index de la méthode à implémenter * @return String La chaîne de la méthode d'assemblage dynamique * / private static string generateMethodcode (string cllasstoproxy = méthodetoToImpl, class interceptorclass, int méthody) {String MethodName = MethodToImtoIn String MethodreTurnType = méthodetoimpl.getReturnType (). GetName (); Class [] Paramètres = méthodetoimpl.getParameterTypes (); Classe [] exceptionTypes = méthodetoimpl.getExceptionTypes (); StringBuffer exceptionBuffer = new StringBuffer (); // Exception Déclaration de méthode d'assemblage if (exceptionTypes.Length> 0) exceptionBuffer.append ("lance"); for (int i = 0; i <exceptionTypes.length; i ++) {if (i! = exceptionTypes.length - 1) exceptionBuffer.APPEND (exceptionTypes [i] .getName ()). APPEND (","); else exceptionbuffer.append (exceptionTypes [i] .getName ()); } StringBuffer paramètreBuffer = new StringBuffer (); // Liste des paramètres de la méthode d'assemblage pour (int i = 0; i <paramètres.length; i ++) {class paramètre = paramètres [i]; String ParameterType = Parameter.GetName (); // dynamique Spécification du nom de la variable de la chaîne de paramètre de méthode refName = "a" + i; if (i! = Parameters.Length - 1) ParameterBuffer.APPEND (ParameterType) .APPEND ("+ RefName) .APPEND (", "); else ParameterBuffer.APPEND (ParameterType) .append (" + RefName); } StringBuffer MethodDeclare = new StringBuffer (); // Déclaration de méthode, puisque c'est une méthode qui implémente l'interface, c'est la méthode publiquedeclare.append ("public") .Apnd (methodreTurnType) .Apnd ("") .Apnd (méthodyName) .Apnd ("("). String interceptorImplName = interceptorclass.getName (); // MethodBody MethodDeclare.Apend (interceptor_handler_interface) .append ("interceptor = new") .append (interceptorimplname) .append ("(); / n"); // Reflection Call Interceptor Interface de l'utilisateur MethodDeclare.APPEnd ("Objet returnObj = interceptor.invoke (class.forname (/" "+ classtoproxy +" / "). NewInstance (), class.forname (/" "+ Classtoproxy +" / "). if (paramètres.Length> 0) MethodDeclare.APPEND ("Nouveau objet [] {"); pour (int i = 0; i <paramètres MethodDeclare.APPEND ("($ w) a" + i + ","); else methoddeclare.append ("($ w) a" + i);} if (paramètres.Length> 0) MethodDeclare.APPEND ("}); / n"); else methoddeclare.append ("null); / n"); // enveloppe la valeur de retour de l'intercepteur d'appel if (methodtoimpl.getReturnType (). IsPrimitive ()) {if (methodtoimpl.getReturnType (). Equals (boolean.type)) methoddeclare.append ("return ((boolean) returnObj) .boolEvalue (); / 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 (methethtoimpl.getReturnType (). equals (double.type)) méthodeDeclare.APPEND ("return (" return ("return (" return ("return (); / n"); else if (methodtoimpl.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 (" methodtoImpl.GetTratype (). MethodDeclare.Apend ("return (" return ("return (" return ("return (" methodtoimplt.getReturnType (). equals (double.type)) methoddeclare.append ("return (" return ("return (" return.type)) MethodDeclare.APPEND ("return (" return ("return (" return ("methodtoimpl.getReturnType (). Equal ((double) returnObj) .DoubleValue (); / n"); else if (methodtoilt.getReturnType (). ((Caractères) returnObj) .CharValue (); / n "); else if (methodtoimpl MethodDeclare.APPEnd ("return (byte) returnObj) .ByteValue (); / n"); else if (methodtoimpl.getReturnType (). MethodreTurnType + ") returnObj; / n");} methoddeclare.append ("}"); Classe, le nom de classe de la classe proxy est requis, la classe d'implémentation interceptrice définie par l'utilisateur nom de classe proxyarithmétique = myproxyimpl.newproxyinstance ("com.test.arithmeticinterface", "com.test.arithmetic", "com.test.interceptorylem"); (ArithmeticInterface) Proxyarithmétique) .add (a, b); ((arithmeticinterface) proxyarithmétique) .Sub (a, b);Imprimez le code pour implémenter dynamiquement l'interface comme suit:
public int add (int a0, int a1) {com.test.interceptorhandler interceptor = new com.test.interceptorhandlerimpl (); objet returnObj = interceptor.invoke (class.forname ("com.test.arithmetic"). newinstance (), class.forname ("com.test.arithmetic"). GetMethods () [0] Objet [] {($ w) a0, ($ w) a1}); return ((Integer) returnObj) .IntValue ();} public int sub (int a0, int a1) {com.test.interceptorhandler interceptor = new com.test.interceptorandlerImpl interceptor.invoke (class.forname ("com.test.arithmetic"). newInstance (), class.forname ("com.test.arithmetic"). getMethods () [1], nouvel objet [] {($ w) a0, ($ w) a1}); return ((intég) returnOBJ) .Intvalue ();}; Ce qui précède est une introduction détaillée au mécanisme de mise en œuvre dynamique de la procuration en Java, et j'espère que cela sera utile à l'apprentissage de chacun.