Analyse approfondie du principe AOP du framework Spring ! Sélection du blog CSDN

Auteur | GitChat

Rédacteur en chef | Guo Rui

Réalisé | Blogue du RPSC

L'AOP (Aspect Oriented Programming) est l'un des composants de base du framework Spring et complète la POO (Object-Oriented Programming) en considérant une autre structure de programme. L'unité clé de la modularité dans la POO est la classe, tandis que dans l'AOP l'unité de la modularité est l'aspect. C'est-à-dire AOP concentrer sur Ce n'est plus une classe, mais un ensemble de comportements au sein d'une classe qui nécessitent des capacités communes.

Cet article comprend principalement :

  • Expliquez la comparaison simple entre OOP et AOP, ainsi que les termes de base de l'AOP, tels que point coupé, conseil, point de jonction, etc. qui sont souvent demandés dans les entretiens.

  • Expliquez les principes et les différences entre le proxy dynamique JDK et le proxy CGLIB qui sont souvent demandés dans les entretiens.

  • Expliquer le principe d'implémentation d'AOP basé sur Schema dans le framework Spring.

  • Expliquer comment implémenter la gestion des transactions basée sur AOP dans le framework Spring.

Concepts de base de l'AOP

Nous savons que l'unité clé de la modularisation en POO est la classe. La classe encapsule le comportement et l'état d'une classe d'objets. Lorsque plusieurs classes ont des attributs et des comportements communs, nous encapsulons ces éléments communs dans une classe de base, puis dans plusieurs classes Les classes peuvent réutiliser ces choses communes en héritant de la classe de base, et le polymorphisme peut être utilisé si les sous-classes ont besoin de personnaliser le comportement de la classe de base. Les classes sont utilisées dans la POO pour fournir l'encapsulation, l'héritage et le polymorphisme.

Lorsque nous devons ajouter une logique non métier commune à certains comportements existants de plusieurs classes non liées, par exemple, lorsque nous devons compter le temps d'exécution de certaines méthodes métier, la pratique précédente doit compter les comportements chronophages Write time- consommant du code. En POO, le code qui n'implique pas d'activité et est dispersé dans plusieurs classes est appelé code transversal. L'inconvénient de cette méthode en POO est que le comportement de la logique métier est affecté. Interférence de code consommatrice de temps de calcul (logique métier le comportement doit se concentrer uniquement sur les affaires), le deuxième inconvénient est que le code chronophage en temps de calcul ne peut pas être réutilisé.

Dans AOP, l'unité modulaire est l'aspect (Aspect), qui encapsule les comportements communs affectant plusieurs classes dans des modules réutilisables, puis vous pouvez décider quand intercepter quels comportements de quelles classes sont exécutés (cutting point), et utiliser le comportement (notification) dans le module réutilisable encapsulé pour améliorer le comportement métier intercepté sans modifier le code du module métier. Aspect en est une description abstraite.

Il existe les concepts de base suivants dans AOP :

  • Point de jonction : un point pendant l'exécution du programme, par exemple lorsqu'une méthode est exécutée ou qu'une exception est gérée. Dans Spring AOP, un point de jointure représente toujours l'exécution d'une méthode.

  • Conseil : Le conseil est ce qu'un aspect fait à un point de jonction particulier. La notification est divisée en notification avant l'exécution de la méthode, notification après l'exécution de la méthode, notification surround, etc. De nombreux frameworks AOP (y compris Spring) modélisent les conseils en tant qu'intercepteurs, maintenant une série d'intercepteurs (formant une chaîne d'intercepteurs) autour d'un point de jonction, améliorant les méthodes du point de jonction.

  • Pointcut : une expression de prédicat qui correspond à un point de jointure. Le conseil est associé à une expression pointcut et fonctionne sur n'importe quel point de jonction (par exemple, l'exécution d'une méthode avec un nom spécifique) qui correspond au pointcut. Les pointcuts sont le concept d'expressions qui correspondent aux points de jonction, sont au cur d'AOP, et Spring utilise AspectJ comme langage d'expression pointcut par défaut.

  • Aspect: C'est un modulaire qui s'étend sur plusieurs classes concentrer sur Point, qui est une abstraction de conseils et pointcut, qui définit un pointcut pour correspondre au point de jonction, c'est-à-dire que les méthodes qui doivent être interceptées doivent être définies ; Il définit une série de annonce de (Conseil) est utilisé pour améliorer la méthode interceptée ;

  • Objet cible : L'objet notifié par un ou plusieurs aspects (Aspect), c'est-à-dire l'objet qui doit être intercepté par AOP pour améliorer la méthode (par notification), également appelé objet notifié. En raison de l'utilisation de proxys d'exécution dans AOP, l'objet cible est toujours l'objet proxy.

  • Proxy AOP (Proxy AOP): Afin de réaliser la fonction d'aspect (Aspect), un objet est créé à l'aide du framework AOP. Dans le framework Spring, un proxy AOP fait référence soit à un proxy dynamique JDK, soit à un proxy CgLIB.

  • Tissage (tissage): est le processus d'application d'aspects à l'objet cible, qui peut être effectué au moment de la compilation (par exemple, à l'aide du compilateur AspectJ), du chargement de la classe ou de l'exécution. Spring AOP, comme les autres frameworks Java AOP purs, effectue l'implantation au moment de l'exécution.

  • Adv est ou: Ce concept a été introduit à partir du support AOP de Spring 1.2, un Adv est ou équivaut à un petit aspect, la différence est qu'il n'a qu'un seul conseil (Conseil), Adv est ou est souvent rencontré dans la gestion des transactions, qui sera discuté plus tard.

Par rapport à la POO, la PAO présente les avantages suivants :

  • Le code métier est plus concis. Par exemple, lorsque vous devez faire quelque chose avant et après un comportement métier, il vous suffit de configurer les aspects avant et après le comportement pour le traitement, sans modifier le code du comportement métier.

  • La logique d'aspect a une bonne encapsulation et peut être réutilisée. Par exemple, nous pouvons encapsuler la logique de journalisation en tant qu'aspect, puis nous pouvons configurer l'aspect sur plusieurs méthodes de plusieurs classes liées ou non liées.

Principes et différences entre le proxy dynamique JDK et le proxy CGLIB

Dans Spring, le proxy AOP est implémenté à l'aide du proxy dynamique JDK et du proxy CGLIB. Par défaut, si l'objet cible est une interface, le proxy dynamique JDK est utilisé. Sinon, CGLIB est utilisé pour générer des classes de proxy. Cette section présente brièvement les principes et les différences entre ces deux procurations. .

Proxy dynamique JDK

Étant donné que le proxy JDK doit servir de proxy à l'interface, écrivez d'abord une classe d'interface :

interface publique UserServiceBo {

entier public ajouter ;

}

Ensuite, implémentez l'interface comme suit :

classe publique UserServiceImpl implémente UserServiceBo {

@Passer outre

entier public ajouter {

System.out.println("--------------------ajouter---------------------- ");

renvoie 0 ;

}

}

Le proxy dynamique JDK doit implémenter l'interface InvocationHandler, voici une classe d'implémentation d'InvocationHandler :

classe publique MyInvocationHandler implémente InvocationHandler {

cible d'objet privé ;

Publique MyInvocationHandler (Object cible) {

super;

e est .target = cible ;

}

@Passer outre

appel d'objet public (proxy d'objet, méthode de méthode, arguments d'objet) throws Throwable {

//(1)

System.out.println("-----------------begin "+method.getName+"-----------------") ;

//(2)

Résultat de l'objet = method.invoke(target, args);

//(3)

System.out.println("-----------------end "+method.getName+"-----------------") ;

retourner le résultat ;

}

Objet public getProxy{

//(4)

return Proxy.newProxyInstance(Thread.currentThread.getContextClassLoader, target.getClass.getInterfaces, th est );

}

}

Créez une classe de test :

Vide public statique principale (arguments de chaîne) {

//(5) Activez ce commutateur pour enregistrer la classe proxy générée sur le disque

System.getProperties.put("sun.m est c.ProxyGenerator.saveGeneratedFiles", "true");

//(6) Créer l'objet cible (objet proxy)

Service UserServiceBo = nouveau UserServiceImpl ;

//(7) Créer une instance de InvocationHandler et transmettre l'objet proxy

Gestionnaire MyInvocationHandler = new MyInvocationHandler(service);

//(8) Générer une classe proxy

Proxy UserServiceBo = (UserServiceBo) handler.getProxy ;

proxy.add ;

}

Le code (6) crée un objet d'instance de UserServiceImpl, qui est l'objet cible à proxy.

Le code (7) crée une instance de InvocationHandler et transmet le service d'objet cible du proxy à la cible de la variable interne.

Code (8) appelle la méthode getProxy de MyInvocationHandler pour générer un objet proxy à l'aide de JDK.

Code (5) Définissez la variable de propriété système sun.m est c.ProxyGenerator.saveGeneratedFiles est vrai, ce qui consiste à enregistrer le fichier bytecode de l'objet proxy généré par le code (8) sur le disque.

Cours au dessus Le code générera la classe $Proxy0.class sous com.sun.proxy du projet. Après décompilation, le code Java de base est le suivant :

package com.sun.proxy ;

importer java.lang.reflect.InvocationHandler ;

importer java.lang.reflect.Method ;

importer java.lang.reflect.Proxy ;

importer java.lang.reflect.UndeclaredThrowableException ;

import proxy.JDK.UserServiceBo ;

classe finale publique $Proxy0

s'étend Procuration

met en oeuvre UserServiceBo

{

méthode statique privée m1 ;

méthode statique privée m3 ;

méthode statique privée m0 ;

méthode statique privée m2 ;

public $Proxy0(InvocationHandler paramInvocationHandler)

{

super(paramInvocationHandler);

}

public final int ajouter

{

essayer

{

//(9) Le premier paramètre est la classe proxy elle-même, le second est la méthode de la classe d'implémentation et le troisième est le paramètre

return h.invoke(th est , m3, );

}

catch (Erreur|RuntimeException localError)

{

jeter localError ;

}

catch (Throwable localThrowable)

{

jeter nouvelle UndeclaredThrowableException(localThrowable);

}

}

...

statique

{

essayer

{

m3 = Class.forName("proxy.JDK.UserServiceBo").getMethod("add", nouvelle classe);

...

revenir;

}

catch (NoSuchMethodException localNoSuchMethodException)

...

}

}

classe proxy $Proxy0 Le h dans le code (9) est l'instance de MyInvocationHandler créée dans la fonction principale, h.invoke(th est , m3, ) est en fait la méthode d'appel de MyInvocationHandler qui est appelée, et cette dernière est déléguée à l'objet proxy pour exécution. Ici, la méthode de l'objet cible peut être interceptée puis améliorée.

De plus, l'objet proxy généré par le code (8) est en fait $Proxy0 Une instance de , lorsque proxy.add est appelé, il s'agit en fait de la classe proxy appelée $Proxy0 La méthode d'ajout de ce dernier est déléguée à la méthode d'appel de MyInvocationHandler, et la méthode d'ajout du service d'objet cible est appelée à l'intérieur de la méthode d'appel.

Puis l'interface (UserServiceBo), l'objet cible (l'objet proxy UserServiceImpl), l'objet proxy ( $Proxy0 ) La relation spécifique entre les trois peut être représentée par la figure suivante :

entrez la description de l'image ici

On peut voir que le proxy dynamique JDK est un proxy pour l'interface ; la classe proxy implémente l'interface et hérite de la classe Proxy ; l'objet cible n'a pas de relation directe avec l'objet proxy, mais ils implémentent tous l'interface et le proxy L'objet est finalement délégué lorsque la méthode est exécutée. L'objet cible exécute une méthode spécifique.

Proxy dynamique CGLIB

Comparé au proxy dynamique JDK pour proxy l'interface, CGLIB doit proxy la classe d'implémentation, ce qui signifie que peu importe si l'objet cible a une interface ou non, CGLIB peut être utilisé pour le proxy.

en dessous de combiner Un code simple qui utilise CGLIB pour proxy dynamiquement la classe d'implémentation à expliquer.

L'utilisation de CGLIB pour le proxy nécessite l'implémentation de MethodInterceptor, créez une classe CglibProxy d'intercepteur de méthode :

la classe publique CglibProxy implémente MethodInterceptor {

//(dix)

Enhancer Enhancer privé = nouvel Enhancer ;

//(11)

Objet public getProxy (classe clazz) {

//(12) Définir l'objet Class de la classe proxy

enhancer.setSuperclass(clazz);

//(13) Définir le rappel de l'intercepteur

enhancer.setCallback(th est );

return enhancer.create;

}

@Passer outre

public Object intercept(Object obj, Method method, Object args, MethodProxy proxy) throws Throwable {

System.out.println(obj.getClass.getName+"."+method.getName);

Résultat de l'objet = proxy.invokeSuper(obj, args);

retourner le résultat ;

}

}

vide public testCglibProxy {

//(14) Générer une classe proxy en local

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/zhuizhumeng xii fr/Téléchargements");

//(15) Générer l'objet cible

Service UserServiceImpl = nouveau UserServiceImpl ;

//(16) Créer un objet CglibProxy

CglibProxy cp = nouveau CglibProxy ;

//(17) Générer une classe proxy

Proxy UserServiceBo = (UserServiceBo) cp.getProxy(service.getClass);

proxy.add ;

}

mettre en place au dessus le code sera dans /Utilisateurs/zhuizhumeng xii fr/Téléchargements Classe proxy de génération d'annuaire UserServiceImpl$$EnhancerByCGLIB$$d0bce05a.class La partie décompilée du code est la suivante :

la classe publique UserServiceImpl$$EnhancerByCGLIB$$d0bce05a étend UserServiceImpl

met en oeuvre Usine

{

vide statique CGLIB$STATICHOOK1

{

//(18) Paramètre vide

CGLIB$emptyArgs = nouvel objet ;

//(19) Obtenir la liste des méthodes d'ajout de UserServiceImpl

Méthode tmp191_188 = ReflectUtils.findMethods(new String { "add", "I" }, (localClass2 = Class.forName("zlx.cglib.zlx.UserServiceImpl")).getDeclaredMethods);

Méthode CGLIB$add$0$ = tmp191_188 ;

//(20) Créer CGLIB$add$0, selon créer un MethodProxy

CGLIB$add$0$Proxy = MethodProxy.create(localClass2, localClass1, "I", "add", "CGLIB$add$0");

}

statique

{

CGLIB$STATICHOOK1 ;

}

//(vingt-et-un)

entier final CGLIB$add$0

{

retourne super.add ;

}

//(vingt-deux)

public final int ajouter

{

...

//(23) Obtenir l'intercepteur, voici l'instance de CglibProxy

MethodInterceptor tmp17_14 = th est .CGLIB$CALLBACK_0 ;

si (tmp17_14 != )

{ //(24) Appel de la méthode d'interception de l'intercepteur

Objet tmp36_31 = tmp17_14.intercept(th est , CGLIB$add$0$Method, CGLIB$emptyArgs, CGLIB$add$0$Proxy);

tmp36_31 ;

return tmp36_31 == ?0 : ((Number)tmp36_31).intValue;

}

retourne super.add ;

}

}

Le code (18) crée un CGLIB$emptyArgs , étant donné que la méthode add n'a pas de paramètres d'entrée, le nombre d'objets Object créés ici est 0, qui est le troisième paramètre de l'interception de l'intercepteur CglibProxy.

Le code (19) obtient la liste des méthodes d'ajout de UserServiceImpl et attribue la seule méthode à Méthode CGLIB$add$0$ , cet objet est le deuxième paramètre d'interception de l'intercepteur CglibProxy.

Le code (20) crée un objet MethodProxy. Lors de l'appel de la méthode invokeSuper de l'objet MethodProxy, il appellera directement l'objet proxy CGLIB$ajouter$0 , c'est-à-dire en appelant directement la méthode add de la classe parent UserServiceImpl, ce qui évite un appel de réflexion, et l'objet MethodProxy créé est le quatrième paramètre de l'interception de l'intercepteur CglibProxy.

Le code (22) est la méthode d'ajout de la classe proxy, et le proxy d'objet proxy généré par le code (17) est UserServiceImpl$$EnhancerByCGLIB$$d0bce05a Une instance de , lorsque la méthode proxy.add est appelée, le code (22) est appelé. À partir du code (22), on peut voir que la méthode d'interception de l'intercepteur CglibProxy est appelée en interne, et le premier paramètre du intercept est l'objet proxy lui-même.

Puis l'interface (UserServiceBo), l'objet cible (l'objet proxy UserServiceImpl), l'objet proxy ( UserServiceImpl$$EnhancerByCGLIB$$d0bce05a ) La relation spécifique entre les trois peut être représentée par la figure suivante :

entrez la description de l'image ici

On peut voir que l'interface n'a rien à voir avec l'objet proxy, l'objet proxy hérite de l'objet cible et implémente l'interface Factory.

Résumer

Le mécanisme de proxy dynamique du JDK ne peut proxy que les interfaces. Le principe est de générer dynamiquement une classe proxy. Cette classe proxy implémente l'interface de l'objet cible. L'objet cible et la classe proxy implémentent l'interface, mais les objets Class de la cible objet et la classe proxy sont Ils ne sont pas identiques, donc les deux ne peuvent pas être attribués l'un à l'autre.

CGLIB est un proxy pour l'objet cible lui-même, donc peu importe si l'objet cible a une interface ou non, il peut proxy l'objet cible. Le principe est d'utiliser l'outil de génération de bytecode pour générer une classe proxy qui hérite de l'objet cible dans mémoire, puis créez une instance d'objet proxy. . Étant donné que la classe parente de la classe proxy est l'objet cible, la classe proxy peut être affectée à l'objet cible. Naturellement, si l'objet cible a une interface, l'objet proxy peut également être affecté à l'interface.

Le bytecode des classes proxy générées dans le proxy dynamique CGLIB est plus complexe que JDK.

JDK utilise le mécanisme de réflexion pour appeler la méthode de la classe cible, et CGLIB utilise la méthode de type index pour appeler directement la méthode de la classe cible, de sorte que la vitesse du proxy dynamique JDK générant une classe proxy est plus rapide que celle de CGLIB, mais la vitesse d'exécution est inférieure à celle de CGLIB, et le proxy JDK Seuls les objets cibles avec des interfaces peuvent être proxy.

Principe de mise en uvre AOP basé sur un schéma dans le framework Spring

Spring propose deux façons de prendre en charge l'AOP : l'AOP basé sur un schéma et l'AOP basé sur des annotations.

L'AOP basé sur le schéma vous permet de configurer la fonctionnalité d'aspect dans un format XML, Spring 2.0 prend en charge la nouvelle balise d'espace de noms "aop" pour définir les aspects, et l'AOP basé sur les annotations vous permet de configurer les aspects à l'aide du style @Aspect.

Cet article parlera d'abord du principe de mise en uvre de l'AOP basé sur le schéma.

AOP est facile à utiliser

Un aspect est en fait un aspect défini dans Spring application Un objet Java normal dans le contexte de la licence, lorsque l'aspect configuration améliore l'objet cible, le format de configuration suivant est généralement utilisé :

< !--(1) -- >

< bean id="helloService" class="zlx.test.aop.HelloServiceBoImpl" / >

< !--(2) -- >

< bean id="monAspect" class="zlx.test.aop.MonAspect" / >

< aop:config >

< !--(3) -- >

< aop:pointcut id="pointcut"

expression="exécution(* *..*BoImpl.sayHello(..))"/ >

< !--(4) -- >

< aop:aspect ref="monAspect" >

< !--(4.1) -- >

< aop:before pointcut-ref="pointcut" method="beforeAdvice" / >

< !--(4.2) -- >

< aop:after pointcut="execution(* *..*BoImpl.sayHello(..))"

method="afterAdvice" / >

< !--(4.3) -- >

< aop:après-retour

pointcut="execution(* *..*BoImpl.sayHelloAfterReturn(..))" method="afterReturningAdvice"

arg-names="contenu" retournant="contenu" / >

< !--(4.4) -- >

< aop:after-throwing pointcut="execution(* *..*BoImpl.sayHelloThrowExecption(..))"

method="afterThrowingAdvice" arg-names="e" throwing="e" / >

< !--(4.5) -- >

< aop:around pointcut="execution(* *..*BoImpl.sayHelloAround(..))"

method="autourConseils" / >

< /aop:aspect >

< /aop:config >

< !--(5) -- >

< bean id = "tracingInterceptor" class="zlx.test.aop.TracingInterceptor"/ >

< !--(6) -- >

< aop:config >

< aop:pointcut id="pointcutForadV est ou" expression="execution(* *..*BoImpl.sayHelloAdv est ou(..))" / >

< aop:adv est ou pointcut-ref="pointcutForadV est ou" advisor-ref="tracingInterceptor" / >

< /aop:config >

Code (1) Créez un objet cible (Target object) à enrichir par AOP. Le code de HelloServiceBoImpl est le suivant :

classe publique HelloServiceBoImpl implémente BonjourServiceBo {

@Passer outre

vide public dis bonjour (Contenu de la chaîne) {

System.out.println("sayHello:" + content);

}

@Passer outre

chaîne publique sayHelloAround (contenu de la chaîne) {

System.out.println("sayHelloAround:" + content);

renvoyer le contenu ;

}

@Passer outre

chaîne publique sayHelloAfterReturn (contenu de la chaîne) {

System.out.println("sayHelloAround:" + content);

renvoyer le contenu ;

}

@Passer outre

vide public sayHelloThrowException {

System.out.println("sayHelloThrowExecption");

throw new RuntimeException("bonjour je suis une exception");

}

}

interface publique HelloServiceBo {

vide public dis bonjour (Contenu de la chaîne) ;

chaîne publique sayHelloAround (contenu de la chaîne);

chaîne publique sayHelloAfterReturn (contenu de la chaîne);

vide public sayHelloThrowException ;

}

L'essence du code (2) est de définir une série d'aspects à utiliser annonce de La méthode (Advice) permet d'enrichir la méthode de l'objet cible (Target object) Le code de MyAspect est le suivant :

classe publique MonAspect {

vide public avantConseils (Contenu de la chaîne) {

System.out.println("---avant conseil "+ "---");

}

vide public aprèsConseil (JoinPointjp) {

System.out.println("---après conseil " + jp.getArgs.toString+"---");

}

public Object afterReturningAdvice(Object value) {

System.out.println("---afterReturning advisor " + value+"---");

valeur de retour + "ha" ;

}

vide public aprèslancerConseils (Exception e) {

System.out.println("---après avoir lancé une exception de conseil :" + e+"---");

}

public Object aroundAdvice(ProceedingJoinPoint pjp) lance Throwable {

Objet obj = pjp.getArgs ;

Contenu de la chaîne = (chaîne) obj ;

System.out.println("---avant l'exécution de sayHelloAround---");

Chaîne retVal = (chaîne) pjp.proceed ;

System.out.println("---après sayHelloAround execute---");

return retVal+ "suffixe" ;

}

}

Le code (3) définit un point coupé, voici le * *..*BoImpl La méthode nommée sayHello dans la classe de l'expression est interceptée.

Le code (4) définit un aspect, et plusieurs intercepteurs peuvent être définis dans un aspect, dont (4.1) définit un pré-intercepteur, qui est connecté au point de connexion (méthode) qui satisfait le point de coupure défini dans le code (3). Intercepter et utiliser la définition dans MyAspect annonce de Méthode avantConseils d'amélioration fonctionnelle. Parmi eux (4.2) définit un post-intercepteur (enfin), qui satisfait la exécution(* *..*BoImpl.sayHello(..)) Méthode de point de jonction conditionnelle utilisant MyAspect dans annonce de La méthode afterAdvice est améliorée. Parmi eux (4.3) définit un post-intercepteur, qui satisfait la exécution(* *..*BoImpl.sayHelloAfterReturn(..)) Méthode de point de jonction conditionnelle utilisant MyAspect dans annonce de La fonction afterReturningAdvice est améliorée.Ce post-connecteur est différent de (4.2) en ce que si la méthode interceptée lève une exception, l'intercepteur ne sera pas exécuté, tandis que l'intercepteur de (4.2) sera toujours exécuté. Parmi eux (4.4) définit un intercepteur qui intercepte l'exception lorsque la méthode interceptée lève une exception, et quelles méthodes sont interceptées par exécution(* *..*BoImpl.sayHelloThrowExecption(..)) décider, notamment annonce de La méthode est la méthode afterThrowingAdvice dans MyAspect. où (4.5) satisfait exécution(* *..*BoImpl.sayHelloAround(..)) Méthode de point de jonction conditionnelle utilisant MyAspect dans annonce de La méthode aroundAdvice est améliorée.

Le code (5) crée un intercepteur de méthode, qui est une notification, avec le code suivant :

class TracingInterceptor implémente MethodInterceptor {

objet public invoque (MethodInvocation i) lance Throwable {

System.out

.println("---method " + i.getMethod + " est appelé sur " + i.getTh est + " avec arguments " + i.getArguments+"---");

Objet ret = i.proceed ;

System.out.println("---method " + i.getMethod + " renvoie " + ret+"---");

retour ret;

}

}

Le code (6) crée une nouvelle balise aop:config, qui crée d'abord un pointcut puis un adv à l'intérieur est ou (une petite tranche), ce qui correspond à annonce de La méthode est tracingInterceptor, et le pointcut correspondant est pointcutForadV est ou.

Il convient de noter que pour pouvoir utiliser les balises aop sous l'espace de noms AOP, vous devez inclure le schéma spring-aop suivant dans le XML :

< ?xml version="1.0" encodage="UTF-8" ? >

< haricots xmlns=" http :// www. springframework.org/schema/beans"

xmlns:xsi=" http :// www. w3.org/2001/XMLSchema-instance"

xmlns:aop=" http :// www. springframework.org/schema/aop"

xsi:schemaLocation="

http :// www. springframework.org/schema/beans http :// www. springframework.org/schema/beans/spring-beans-4.3.xsd

http :// www. springframework.org/schema/aop http :// www. springframework.org/schema/aop/spring-aop-4.3.xsd" >

< !-- < haricot/ > définitions ici -- >

< /des haricots >

Paquet au dessus Une fois la configuration collectée et placée dans le fichier de configuration beanaop.xml, écrivez le code suivant pour tester :

classe publique TestAOP {

public static final String xmlpath = "beanaop.xml" ;

Vide public statique principale (arguments de chaîne) {

ClassPathXmlApplicationContext cpxa = nouveau ClassPathXmlApplicationContext(xmlpath);

HelloServiceBo serviceBo = cpxa.getBean("helloService",HelloServiceBo.class);

serviceBo.sayHello("Je t'aime");

Résultat de la chaîne = serviceBo.sayHelloAround("Je t'aime");

System.out.println(result);

result = serviceBo.sayHelloAfterReturn("Je t'aime");

System.out.println(result);

serviceBo.sayHelloAdv est ou("Je t'aime");

serviceBo.sayHelloThrowExecption ;

}

}

Analyse de principe

Analyse des balises aop:config

Puisque cet article explique le principe d'AOP basé sur la configuration XML, commençons par parser l'aop:config configuré en XML. Regardez d'abord comment la classe ConfigBeanDefinitionParser du framework Spring analyse la balise aop:config.Le diagramme de séquence principal est le suivant :

entrez la description de l'image ici

Code (3) enregistre un AspectJAwareAdv est La classe orAutoProxyCreator est envoyée au conteneur Spring IOC. La fonction de cette classe est de créer automatiquement des classes proxy, dont nous parlerons plus loin. Il convient de noter ici que dans reg est La méthode useClassProxyingIfNecessary de terAspectJAutoProxyCreatorIfNecessary analyse les valeurs des attributs proxy-target-class et expose-proxy dans la balise aop:config. Le code est le suivant :

vide statique privé useClassProxyingIfNecessary (BeanDefinitionReg est essayez reg est essayez, @able Element sourceElement) {

// Analyser l'attribut proxy-target-class

si (sourceElement != ) {

boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)); // Définissez la valeur de l'attribut proxy-target-class sur AspectJAwareAdv est dans orAutoProxyCreator

si (proxyTargetClass) {

AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(reg est essayer);

}

// Analyser la valeur de l'attribut expose-proxy

boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));

// Définissez la valeur de l'attribut expose-proxy sur AspectJAwareAdv est ouAutoProxyCreator

si (exposerProxy) {

AopConfigUtils.forceAutoProxyCreatorToExposeProxy(reg est essayer);

}

}

}

Pour l'attribut proxy-target-class (faux par défaut), s'il est défini sur vrai, il forcera l'utilisation de CGLIB pour générer une classe proxy ; pour l'attribut expose-proxy (faux par défaut), s'il est défini sur vrai , il appellera une méthode imbriquée dans une classe La méthode est également mandatée, en particulier, si une classe ServiceImpl contient les méthodes a et b, si a appelle b dans a :

vide public un {

e est .b ;

}

Ensuite, par défaut, lorsque la fonction de la méthode a est améliorée, la méthode b à l'intérieur ne sera pas améliorée. Si vous devez également améliorer la méthode b, vous pouvez définir expose-proxy sur true et utiliser la méthode suivante lors de l'appel de b méthode :

vide public un {

((ServiceBo)AopContext.currentProxy).b;

}

Il convient de noter que AopContext.currentProxy renvoie la classe proxy. Si le proxy JDK est utilisé, la classe d'interface doit être utilisée pour la conversion de type ici. S'il s'agit du proxy CGLIB, la conversion de type sera la classe d'implémentation ServiceImpl.

Le code (4) analyse l'élément aop:pointcut dans la balise aop:config. Chaque élément pointcut crée une définition de bean d'AspectJExpressionPointcut et l'enregistre auprès de Spring IOC. Le code principal de parsePointcut est le suivant :

private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {

// Récupère la valeur de l'attribut id de la balise aop:pointcut

ID de chaîne = pointcutElement.getAttribute(ID);

// Récupère la valeur de l'attribut expression de la balise aop:pointcut

Expression de chaîne = pointcutElement.getAttribute(EXPRESSION);

AbstractBeanDefinition pointcutDefinition = ;

essayer {

//Créer une définition de bean de AspectJExpressionPointcut et définir les propriétés

pointcutDefinition = createPointcutDefinition(expression);

pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));

//Si la valeur de l'attribut id de la balise aop:pointcut n'est pas vide, l'id est utilisé comme nom de bean du bean dans le conteneur Spring et le bean est enregistré dans le conteneur Spring.

String pointcutBeanName = id;

si (StringUtils.hasText(pointcutBeanName)) {

parserContext.getReg est essayez.reg est terBeanDefinition(pointcutBeanName, pointcutDefinition);

}

else {//Aucun système ne génère automatiquement un nom et injecte le bean dans le conteneur Spring

pointcutBeanName = parserContext.getReaderContext.reg est terWithGeneratedName(pointcutDefinition);

}

...

}

...

return pointcutDefinition ;

}

Noter: L'expression de la variable membre d'AspectJExpressionPointcut contient la valeur de l'attribut expression de la balise aop:pointcut, qui est utilisée dans le proxying automatique.

Le code (5) est pour aop:adv dans la balise aop:config est ou les éléments sont analysés, chaque adv est L'élément ou crée un DefaultBeanFactoryPointcutAdv est définition de haricot de ou et enregistrée dans Spring IOC, parseAdv est ou le code est le suivant :

vide privé parseAdv est ou (Élément adv est orElement, ParserContext parserContext) {

//Créer DefaultBeanFactoryPointcutAdv est La définition de ou et font référence à des conseils

AbstractBeanDefinition adv est orDef = createAdv est orBeanDefinition(adv est orElement, parserContext);

ID de chaîne = adv est ouElement.getAttribute(ID);

essayer {

...

//Enregistrer DefaultBeanFactoryPointcutAdv est ou au conteneur Spring

Chaîne adv est orBeanName = id ;

si (StringUtils.hasText(adv est ouBeanName)) {

parserContext.getReg est essayez.reg est terBeanDefinition(adv est orBeanName, adv est ouDef);

}

autre {

adv est orBeanName = parserContext.getReaderContext.reg est terAvecNomGénéré(adv est ouDef);

}

//analyse aop:adv est Le point de coupe référencé dans la balise ou et défini sur DefaultBeanFactoryPointcutAdv est Définition de ou

Objet pointcut = parsePointcutProperty(adv est orElement, parserContext);

if (instance pointcut de String) {

adv est orDef.getPropertyValues.add(POINTCUT, new RuntimeBeanReference((String) pointcut));

...

}

...

}

...

}

Noter: Chaque DefaultBeanFactoryPointcutAdv est Dans l'objet ou , le nom de bean du conseil de référence dans la BeanFactory est enregistré via la variable membre advisorBeanName, et le pointcut est enregistré via la variable membre pointcut.

DefaultBeanFactoryPointcutAdv est ou hérité d'Adv est ou interface, il convient de noter ici que advisorBeanName enregistre uniquement le nom de chaîne du conseil, alors comment obtenir le vrai annonce de Qu'en est-il de l'objet, en fait DefaultBeanFactoryPointcutAdv est ou implémente BeanFactoryAware, qui garantit la référence de BeanFactory en interne. Maintenant qu'il existe une BeanFactory, vous pouvez obtenir le bean souhaité en fonction du nom du bean. Pour cela, vous pouvez vous référer à Chat : Démystification des interfaces d'extension communes de Spring Framework (https: //gitbook .cn /gitchat/activité/5a84589a1f42d45a333f2a8e).

Le code (6) analyse l'élément aop:aspect dans la balise aop:config et analyse les sous-éléments de l'élément aop:aspect, et chaque sous-élément créera un AspectJPointcutAdv en conséquence est La définition de bean de or est enregistrée auprès de Spring IOC. Le code de parseAspect est le suivant :

vide privé parseAspect (Élément aspectElement, ParserContext parserContext) {

// Récupère l'attribut id de l'élément aop:aspect

Chaîne aspectId = aspectElement.getAttribute(ID);

// Récupère l'attribut ref de l'élément aop:aspect

Chaîne aspectName = aspectElement.getAttribute(REF);

essayer {

e est .parseState.push(new AspectEntry(aspectId, aspectName));

L est t < Définition du haricot > beanDefinitions = new ArrayL est t < > ;

L est t < BeanReference > beanReferences = new ArrayL est t < > ;

...

//boucle sur toutes les notifications dans l'élément ````aop:aspect````

NodeL est tnodeL est t = aspectElement.getChildNodes ;

conseil booléenFoundAlready = false ;

pour (int je = 0; je < nudL est t.getLength ; i++) {

Nud nud = nudL est t.item(i);

//Déterminer s'il s'agit d'un nud de notification, tel que le front, la dernière notification, etc.

si ( est AdviceNode(nud, parserContext)) {

if (!adviceFoundAlready) {

conseilFoundAlready = true ;

...

beanReferences.add(new RuntimeBeanReference(aspectName));

}

//Créer différents types en fonction des différents types de notification annonce de Objet, finalement encapsulé comme AspectJPointcutAdv est ou une définition de bean et enregistrez-la avec le conteneur Spring

AbstractBeanDefinition adv est orDefinition = parseAdvice(

aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);

beanDefinitions.add(adv est ouDéfinition);

}

}

// Boucle analysant tous les pointcuts dans l'élément ``aop:aspect```` et l'enregistrant avec le conteneur Spring

L est t < Élément > pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);

for (Element pointcutElement : pointcuts) {

parsePointcut(pointcutElement, parserContext);

}

...

}

finalement {

...

}

}

Il convient de noter que différents objets de conseil seront créés à l'intérieur de la fonction parseAdvice en fonction du type de conseil. L'objet de conseil sera créé correspondant au conseil avant comme AspectJMethodBeforeAdvice, et l'objet de conseil sera créé correspondant au conseil après. annonce de L'objet est AspectJAfterAdvice, créé pour les conseils après retour annonce de L'objet est AspectJAfterReturningAdvice, créé pour les conseils après le lancement annonce de L'objet est AspectJAfterThrowingAdvice, créé pour environ conseils annonce de L'objet est AspectJAroundAdvice, et une chose en commun est que ces objets de conseil implémentent tous l'interface MethodInterceptor.

Noter: par AspectJPointcutAdv est L'objet ou maintient une notification par le biais de conseils et maintient un tel point de coupe par point de coupe, AspectJPointcutAdv est ou hérité d'Adv est ou interface.

Jusqu'à présent, le framework Spring a converti toutes les configurations dans les balises aop:config en définitions Bean et les a injectées dans le conteneur Spring.Il convient de noter que plusieurs balises aop:config peuvent être configurées dans la configuration XML correspondant à une application Spring. Ce processus est répété lors de l'analyse d'une balise aop:config.

Génération de classes proxy

(1) Le processus principal généré par la classe proxy

La section précédente a injecté un AspectJAwareAdv lors de l'analyse de la balise aop:config est orAutoProxyCreator classe au conteneur Spring, en fait, cette classe est de réaliser la génération dynamique de la classe proxy, AspectJAwareAdv est orAutoProxyCreator implémente l'interface BeanPostProcessor (cette interface est une interface étendue permettant au framework Spring de faire des choses avant et après l'initialisation du bean. Pour plus de détails, veuillez vous référer à : http ://gitbook .cn /gitchat/activity/5a84589a1f42d45a333f2a8e), il y aura donc la méthode Object postProcessAfterInitialization(@able Object bean, String beanName), examinons le diagramme de séquence d'exécution du code de cette méthode :

entrez la description de l'image ici

Le code (3) Rechercher dans le conteneur Spring peut améliorer le haricot actuel annonce de bean, le code est d'abord exécuté (4) dans le conteneur Spring pour trouver toutes les implémentations d'Adv est Les beans de l'interface ou , c'est-à-dire tous les DefaultBeanFactoryPointcutAdv analysés à partir de la balise aop:config expliquée dans la section précédente est ou et AspectJPointcutAdv est Des instances de ou sont trouvées. Le code (5) examine l'annonce trouvée est Lequel des ou peut être appliqué au bean actuel correspond à une expression pointcut. Le code (6) correspond à Adv est ou de trier selon chaque Adv est La valeur de la méthode getOrder de l'objet ou est triée.

Le code (13) décide ici s'il faut utiliser le proxy dynamique JDK ou CGLIB pour le proxy selon certaines conditions, qui appartiennent au mode stratégie dans le mode conception.

public AopProxy createAopProxy(Adv est edSupport config) lève AopConfigException {

//Si optimise=true ou proxyTargetClass=ture ou si aucune interface proxy n'est spécifiée, utilisez CGLIB pour le proxy

si (config. est Optimiser || config. est ProxyTargetClass || hasNoUserSuppliedProxyInterfaces(config)) {

Classer < ? > targetClass = config.getTargetClass ;

// Si la classe cible est une interface ou si la classe cible est une classe générée à l'aide du proxy dynamique JDK, elle doit être proxy par JDK

si (targetClass. est Interface || Proxy. est ProxyClass(targetClass)) {

renvoie le nouveau JdkDynamicAopProxy(config);

}

//Non, utiliser CGLIB pour le proxy

retourner de nouveaux objets est CglibAopProxy(config);

}

//Utiliser JDK pour le proxy

autre {

renvoie le nouveau JdkDynamicAopProxy(config);

}

Quant à proxyTargetClass, il a été mentionné précédemment qu'il peut être configuré dans la balise aop:config, alors où est optimisé configuré ? En fait, en plus du mode AOP basé sur les balises décrit dans cet article, Spring fournit également un proxy dynamique des manières suivantes :

< bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" >

< propriété name="beanNames" value="*impl" > < /propriété > < !-- ne produit que des proxys pour les beans avec le suffixe "impl" -- >

< propriété name="interceptorNames" value="monAdv est ou" > < /propriété > < !--Bloqueur de méthode personnalisé-- >

< nom de la propriété="optimiser" valeur="true" > < /propriété >

< /haricot >

BeanNameAutoProxyCreator peut utiliser interceptorNames pour améliorer le bean du beanName de la règle spécifiée. Puisqu'il est impossible de déterminer si le bean correspondant a une interface à ce moment, l'optimisation est définie sur true ici, puis lorsque la fabrique de proxy est créée, il est déterminé si la classe proxy est une interface ou non, s'il faut utiliser un proxy JDK ou un proxy CGLIB.

Le code (14) doit appeler spécifiquement JdkDynamicAopProxy ou Objenes est La méthode getProxy de CglibAopProxy obtient la classe proxy. Les deux sections suivantes décrivent comment la générer.

(2) Le proxy dynamique JDK génère un objet proxy

Regardez d'abord la méthode getProxy de JdkDynamicAopProxy :

Objet public getProxy(@able ClassLoader classLoader) {

si (enregistreur. est Débogage activé) {

logger.debug("Création d'un proxy dynamique JDK : source cible est " + le est .adv est ed.getTargetSource);

}

Classer < ? > pro xii edInterfaces = AopProxyUtils .com pleinPro xii edInterfaces(th est .adv est ed, vrai);

findDefinedEqualsAndHashCodeMethods(pro xii edInterfaces);

return Proxy.newProxyInstance(classLoader, pro xii edInterfaces, e est );

}

Étant donné que la classe JdkDynamicAopProxy implémente l'interface InvocationHandler, le troisième paramètre passé lorsque Proxy.newProxyInstance est utilisé pour créer un objet proxy est le est . Jetons un coup d'il à la méthode d'appel de JdkDynamicAopProxy :

appel d'objet public (proxy d'objet, méthode de méthode, arguments d'objet) throws Throwable {

...

essayer {

...

//Obtenir une liste d'intercepteurs pouvant être appliqués à cette méthode

L est t < Objet > chaîne = th est .adv est ed.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

//Créer une méthode d'invocation, qui est une chaîne d'intercepteur, qui est un mode de chaîne de responsabilité

invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);

//Utiliser la chaîne d'intercepteurs pour gérer cette méthode de point de jonction

retVal = invocation.proceed ;

...

retourner retVal ;

}

finalement {

...

}

}

voir ci-dessous au dessus Le diagramme de séquence lors de l'appel de serviceBo.sayHello dans l'exemple de "AOP Simple Use" dans la sous-section "AOP Simple Use" pour approfondir la compréhension :

  • Dans l'exemple de "AOP Simple Use", on ajoute un pré-intercepteur avant l'exécution de la méthode sayHello, et un post-intercepteur après l'exécution de la méthode sayHello ;

  • Lorsque le serviceBo.sayHello est exécuté, la méthode sayHello de la classe proxy est réellement exécutée, elle sera donc interceptée par la méthode d'appel de JdkDynamicAopProxy. Dans la méthode d'appel, la méthode getInterceptorsAndDynamicInterceptionAdvice est d'abord appelée pour obtenir la liste des intercepteurs configurés sur la méthode sayHello, puis un objet ReflectiveMethodInvocation (interne) est créé. Il s'agit d'une chaîne de responsabilité basée sur un tableau de nombres), puis appelle la méthode procced de l'objet pour activer la chaîne d'intercepteurs afin d'améliorer la méthode sayHello. Ici, le pré-connecteur est d'abord appelé pour améliorer sayHello, puis la véritable méthode métier sayHello est appelée, et enfin l'appel Le post-intercepteur est ajouté pour améliorer sayHello.

(3) Le proxy dynamique CGLIB génère un objet proxy

Premier regard sur Objenes est La méthode getProxy de CglibAopProxy :

Objet public getProxy(@able ClassLoader classLoader) {

essayer {

Classer < ? > rootClass = th est .adv est ed.getTargetClass ;

//Créer un rehausseur CGLIB

Enhancer Enhancer = createEnhancer ;

...

enhancer.setSuperclass(proxySuperClass);

enhancer.setInterfaces(AopProxyUtils .com pleinPro xii edInterfaces(th est .adv est éd));

enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);

...

//Obtenir un rappel, principalement DynamicAdv est edInterceptor

Rappels de rappel = getCallbacks(rootClass);

Classer < ? > types = nouvelle classe < ? > ;

pour (int x = 0; x < types.length; x++) {

types = rappels.getClass ;

}

// définir le rappel

enhancer.setCallbackTypes(types);

//Générer la classe proxy et créer une instance proxy

return createProxyClassAndInstance(enhancer, callbacks);

}

catch (CodeGenerationException | IllegalArgumentException ex) {

...

}

...

}

Jetons un coup d'il à l'intercepteur DynamicAdv est La méthode d'interception d'edInterceptor, le code est le suivant :

public Object intercept(Object proxy, Method method, Object args, MethodProxy methodProxy) throws Throwable {

...

SourceCible SourceCible = th est .adv est ed.getTargetSource ;

essayer {

...

//Obtenir une liste d'intercepteurs pouvant être appliqués à cette méthode

L est t < Objet > chaîne = th est .adv est ed.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

Objet retVal ;

//Si la liste des intercepteurs est vide, appel de la méthode métier directement par réflexion

si (chaîne. est Modificateur vide. est Public(method.getModifiers)) {

Objet argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);

retVal = methodProxy.invoke(target, argsToUse);

}

autre {

//Créer une invocation de méthode, en fait, il s'agit d'une chaîne d'intercepteur, ici appeler pour activer la chaîne d'intercepteur pour améliorer la méthode actuelle

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed ;

}

// gérer la valeur de retour

retVal = processReturnType (proxy, cible, méthode, retVal);

retourner retVal ;

}

finalement {

...

}

}

Regardons le diagramme de séquence de la chaîne d'appel lors de l'appel de serviceBo.sayHello dans l'exemple de "AOP Simple Use" pour approfondir votre compréhension :

entrez la description de l'image ici

  • En raison du proxy dynamique JDK utilisé par défaut, pour utiliser CGLIB pour le proxy, vous devez ajouter les attributs suivants à la balise aop:config :

< aop:config proxy-target-class="true" >

  • Le processus d'exécution est similaire au proxy dynamique JDK, qui ne sera pas repris ici.

Comment implémenter la gestion des transactions basée sur AOP dans le framework Spring

Configuration simple des transactions

XML utilise des balises pour configurer les transactions, généralement comme suit :

< aop:config >

< !--(1) -- >

< aop:pointcut id="businessService"

expression="exécution(* com.xyz.my application .service.*.*(..))"/ >

< !--(2) -- >

< aop:adv est ou

pointcut-ref="businessService"

conseil-ref="tx-conseil"/ >

< /aop:config >

< !--(3) -- >

< tx:advice id="tx-avis" >

< tx:attributs >

< tx:nom de la méthode="*" propagation="REQUIS"/ >

< /tx :attributs >

< /tx:conseil >

  • Configuré comme ci-dessus (1), un point coupé avec un identifiant de businessService est configuré pour correspondre aux méthodes permettant d'effectuer des améliorations de transaction ;

  • Configuration (2) configure un adv est ou , en utilisant businessService comme point de départ et tx-advice comme méthode de notification ;

  • La configuration (3) utilise la balise tx:advice pour configurer une notification, c'est le point clé et sera expliqué dans la section suivante.

Remarque : La fonction de cette configuration est d'améliorer la transaction pour la méthode qui satisfait la condition pointcut avec l'ID de businessService et de définir le niveau de propagation de la transaction sur REQUIRED.

Analyse de principe

Analyse de la balise tx:advice

La balise tx:advice est analysée à l'aide de TxAdviceBeanDefinitionParser. Examinons le diagramme de séquence d'analyse :

entrez la description de l'image ici

  • Comme indiqué dans le diagramme de séquence ci-dessus, BeanDefinitionBuilder est un modèle de générateur utilisé pour construire une définition de bean, qui générera éventuellement une instance de TransactionInterceptor ;

  • Étape (5) Analysez toutes les balises tx:method dans la balise tx:attributes en boucle, chaque tx:method correspond à un objet RuleBasedTransactionAttribute, dans lequel la balise tx:method peut non seulement configurer la propagation des transactions, mais également configurer le niveau d'isolement des transactions, délai d'expiration, lecture seule et stratégie de restauration.

  • Étape (12) enregistrez tous les objets RuleBasedTransactionAttribute de la balise de méthode dans une structure de données de carte, étape (13) définissez tous les attributs analysés sur l'objet en mode générateur, étape (14) utilisez l'objet générateur pour créer une définition de bean, étape ( 15) puis enregistrez le haricot dans le conteneur Spring.

Noter: Le rôle de tx:advice est de créer un intercepteur TransactionInterceptor qui gère en interne les informations de configuration des transactions.

Analyse simple du principe d'intercepteur de transaction

Jetons un coup d'il à la méthode d'invocation de TransactionInterceptor :

objet public invoque (invocation finale de MethodInvocation) lance Throwable {

...

return invokeWithinTransaction(invocation.getMethod, targetClass, invocation::proceed);

}

Objet protégé invokeWithinTransaction(Method method, @able Class < ? > classe cible,

invocation finale InvocationCallback) lance Throwable {

//Si la attribut de transaction est , la méthode est non transactionnel.

TransactionAttributeSource tas = getTransactionAttributeSource ;

final TransactionAttribute txAttr = (tas != ? tas.getTransactionAttribute(method, targetClass) : );

final PlatformTransactionManager tm = determineTransactionManager(txAttr);

chaîne finale joinpointIdentification = methodIdentification(method, targetClass, txAttr);

if (txAttr == || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {

// La transaction standard, la transaction interne getTransaction (transaction ouverte) et la transaction commit (commit)/rollback (rollback) sont appelées.

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

Objet retVal = ;

essayer {

// Il s'agit d'une notification surround, appelantprocedWithInvocation pour activer le prochain intercepteur dans la chaîne d'intercepteurs

retVal = invocation.proceedWithInvocation ;

}

attraper (jetable ex) {

// exception d'invocation cible

completeTransactionAfterThrowing(txInfo, ex);

jeter ex;

}

finalement {

cleanupTransactionInfo(txInfo);

}

commitTransactionAfterReturning(txInfo);

retourner retVal ;

}

...

}

Parmi elles, createTransactionIfNecessary est une méthode importante, et son flux de traitement logique interne est le suivant :

entrez la description de l'image ici

Noter: La gestion des transactions Spring est implémentée en configurant un aspect AOP, qui définit un point de coupure pour décider quelles méthodes intercepter, et définit une notification TransactionInterceptor pour améliorer la méthode interceptée. combiner au dessus Organigramme de la méthode TransactionInterceptor combiner Étudions le code source, si nécessaire, nous ouvrirons un chat plus tard pour expliquer l'implémentation des transactions Spring, ainsi que l'isolation et la propagation des transactions.

Résumer

Cet article analyse le principe d'AOP sous la forme d'un aperçu et comment l'intercepteur de transactions utilise AOP pour l'implémenter. En raison de contraintes d'espace, il n'entre pas dans tous les détails d'implémentation pour expliquer. J'espère que les lecteurs pourront se fier à cet article comme un esquisser et approfondir les détails de l'esquisse en fonction du code source. Mener des recherches pour approfondir votre compréhension et votre maîtrise des principes de l'AOP.

les jeunes contemporains « santé faux » de la route - la « usure de la jambe légère vers le bas »?
Précédent
Chine Art Troupe handicapés « My Dream » créée avec succès en Argentine
Prochain
Roi de gloire: les gens souvent confondu avec la peau en cuir d'origine comme un symbole de la mauvaise rue ou tyran?
Pennant Zhang, professeur associé à l'Université Carnegie Mellon: de nouvelles façons système de détection, la réponse physique de la maladie humaine et la mort
Musique Say Goodnight | coeur
Tesla micros, seront vendu?
Roi de gloire: commencer à apprendre pas les mauvaises compétences en héros, il a appris que tourner en rond dans la mauvaise tour
Il était de 51 ans, le code d'écriture au satellite: la vie robuste, jamais peur de la nuit
Roi de gloire: la plus grande étoile d'échecs grande gamme de mouvement? Joueurs: héros avec ce ratio est presque
Roi de gloire: Le pigeon de trois optimisation, assez simple: pas de peau peut être optimisée
Choqué! Hangzhou Pa Moyen feu portable scolaire, le secret est si simple
Flying Dragon, Kunio Wang Jian
Roi de gloire: Luban maîtres reportée à 28 tenue d'inscription en ligne pour savoir à l'avance
stratégie Electrification avant-garde d'Audi e-tron et Q2L e-tron officiellement répertoriés