Qu'est-ce que AOP
AOP (programmation orientée vers l'aspect, programmation orientée vers l'aspect) peut être considérée comme un supplément et une amélioration de la POO (programmation orientée objet). La POO introduit des concepts tels que l'encapsulation, l'héritage et le polymorphisme pour établir une hiérarchie d'objets pour simuler une collection de comportements publics. Lorsque nous devons introduire le comportement public aux objets dispersés, la POO semble impuissante. Autrement dit, la POO vous permet de définir des relations de haut en bas, mais ne convient pas pour définir des relations de gauche à droite. Par exemple, fonction de journalisation. Le code journal est souvent dispersé horizontalement à tous les niveaux d'objets sans aucun rapport avec la fonctionnalité principale de l'objet auquel il est dispersé. Il en va de même pour d'autres types de code, tels que la sécurité, la gestion des exceptions et la transparence. Ce type de code non pertinent dispersé partout est appelé code transversal. Dans la conception OOP, il provoque beaucoup de duplication de code, ce qui n'est pas propice à la réutilisation de chaque module.
AOP technology, on the contrary, uses a technique called "crosscutting" to dissect the inside of the encapsulated object and encapsulate the common behaviors that affect multiple classes into a reusable module and name it "Aspect", i.e. The so-called "aspect", simply put, encapsulates logic or responsibilities that are not related to the business but are called jointly by the business module, which facilitates reducing the system's duplicate code, Réduire le couplage entre les modules et promouvoir l'opérabilité et la maintenabilité futures. AOP représente une relation horizontale. Si «l'objet» est un cylindre creux, encapsulant les propriétés et le comportement de l'objet; Ensuite, la méthode de programmation orientée vers l'aspect est comme une lame tranchante, coupant ces cylindres creux pour obtenir des informations internes. La section découpée est le soi-disant "visage". Il a ensuite restauré ces sections coupées avec ses mains intelligentes sans laisser de trace.
En utilisant la technologie "transversal", AOP divise le système logiciel en deux parties: préoccupation principale et préoccupation croisée. Le principal processus de traitement commercial est l'objectif principal, et la partie qui a peu à voir avec elle est l'objectif transversal. Une caractéristique des préoccupations croisées est qu'ils se produisent souvent dans plusieurs préoccupations de base et sont fondamentalement similaires partout. Par exemple, l'authentification de l'autorisation, la journalisation et le traitement des transactions. Le rôle de l'AOP est de séparer diverses préoccupations dans le système et de séparer les préoccupations de base des préoccupations transversales. Comme l'a dit Adam Magee, architecte de solution senior chez Avanade,, l'idée principale de l'AOP est de «séparer la logique métier dans l'application des services communs qui le soutiennent».
La technologie pour implémenter AOP est principalement divisée en deux catégories: l'une consiste à utiliser la technologie de proxy dynamique et à utiliser la méthode d'interception des messages pour décorer le message pour remplacer l'exécution du comportement d'objet d'origine; L'autre consiste à utiliser la méthode de tissage statique pour introduire une syntaxe spécifique pour créer des "faces", afin que le compilateur puisse tisser le code lié aux "faces" pendant la compilation.
Scénarios d'utilisation AOP
AOP est utilisé pour encapsuler des préoccupations croisées, qui peuvent être utilisées dans les scénarios suivants:
Autorisation d'authentification
Mise en cache de mise en cache
Contexte passant le contenu
Gestion des erreurs
Chargement paresseux
Débogage
journalisation, traçage, profilage et surveillance
Optimisation des performances
Persévérance de persévérance
Regroupement des ressources
Synchronisation
Transactions
Concepts liés à l'AOP
Aspect: Une modularité d'une focalisation qui peut réduire davantage plusieurs objets. La gestion des transactions est un bon exemple de préoccupations croisées dans les applications J2EE. L'aspect est mis en œuvre à l'aide de Spring's Advisor ou Interceptor.
JoinPoint: un point clair lors de l'exécution du programme, tel qu'un appel de méthode ou une exception spécifique lancée.
Conseils: Actions effectuées par le cadre AOP à un point de connexion spécifique. Divers types de notifications incluent "autour", "avant" et "lance" les notifications. Les types de notification sont discutés ci-dessous. De nombreux cadres AOP, y compris le printemps, utilisent les intercepteurs comme modèles de notification pour maintenir des points de connexion "ronds" de la chaîne interceptrice. Quatre conseils sont définis au printemps: bereadvice, AfterAdvice, Throwadvice et DynamiCinTroductionAdvice
PointCut: Spécifie une collection de points de connexion auxquels la notification sera déclenchée. Le cadre AOP doit permettre aux développeurs de spécifier des points d'entrée: par exemple, en utilisant des expressions régulières. Spring définit l'interface Pointcut, qui est utilisée pour combiner MethodMatcher et ClassFilter, qui peut être comprise clairement via le nom. MethodMatcher est utilisé pour vérifier si la méthode de la classe cible peut être utilisée pour appliquer cette notification, tandis que ClassFilter est utilisée pour vérifier si Pointcut doit être appliqué à la classe cible.
Introduction: ajoutez une méthode ou un champ à la classe qui est notifiée. Spring permet l'introduction de nouvelles interfaces à tout objet notifié. Par exemple, vous pouvez simplifier la mise en cache à l'aide d'une introduction qui permet à n'importe quel objet d'implémenter l'interface ismodifiée. Pour utiliser l'introduction dans le printemps, vous pouvez utiliser DelegatingingIntroduction Interceptor pour implémenter les notifications et utiliser defaultIntroductionAdvisor pour configurer l'interface pour implémenter des classes de conseils et de proxy.
Objet cible: un objet contenant le point de connexion. Également connu comme un objet notifié ou proxy. Pojo
Proxy AOP: un objet créé par le cadre AOP, contenant des notifications. Au printemps, le proxy AOP peut être un proxy dynamique JDK ou un proxy CGLIB.
Tissage: assembler pour créer un objet notifié. Cela peut être fait au moment de la compilation (par exemple en utilisant le compilateur AspectJ) ou à l'exécution. Le printemps, comme d'autres frameworks Java AOP purs, termine le tissage au moment de l'exécution.
Composants Spring AOP
Le diagramme de classe suivant répertorie les principaux composants AOP au printemps
Comment utiliser Spring AOP
Spring AOP peut être utilisé dans les fichiers de configuration ou les façons de programmation.
La configuration peut être effectuée via le fichier XML, et il y a environ quatre façons:
1. Configurez ProxyFactoryBean, définissez explicitement les conseillers, les conseils, la cible, etc.
2. Configurer AutoproxyCreator. De cette façon, le haricot défini est toujours utilisé comme auparavant, mais ce que vous obtenez du conteneur est en fait un objet proxy.
3. Configurer via <aop: config>
4. Configurer via <aop: aspectj-autoproxy> et utilisez des annotations Aspectj pour identifier les notifications et les points d'entrée
Vous pouvez également utiliser ProxyFactory directement pour utiliser Spring AOP par programme. Grâce aux méthodes fournies par ProxyFactory, vous pouvez définir des objets cibles, des conseillers et d'autres configurations connexes, et enfin obtenir l'objet proxy via la méthode getProxy ().
Des exemples d'utilisation spécifiques peuvent être Google. omis ici
Génération d'objet proxy Spring AOP
Spring fournit deux façons de générer des objets proxy: jdkproxy et cglib. La méthode de génération spécifique est déterminée par AOPProxyfactory basée sur la configuration de l'objet conseillé. La stratégie par défaut consiste à utiliser JDK Dynamic Proxy Technology Si la classe cible est une interface, sinon utilisez CGLIB pour générer le proxy. Étudions comment Spring utilise JDK pour générer des objets proxy. Le code de génération spécifique est placé dans la classe JDKDYMICAOPPROXY, et le code pertinent est directement ajouté:
/ ** * <l> * <li> Obtenez l'interface à implémenter par la classe proxy. En plus de la configuration dans l'objet conseillé, SpringProxy sera également ajouté, conseillé (opaque = false) * <li> Vérifiez s'il existe une interface qui définit l'égalité ou le code de hash dans l'interface obtenue ci-dessus * <li> Appel (logger.isdebugeNabled ()) {logger.debug ("Création de proxy dynamique JDK: la source cible est" + this.advised.gettargetSource ()); } Class [] proxiedInterfaces = aopproxyutils.complèteproxiedInterfaces (this.adisvised); FindDefinedEqualsandHashCodeMethods (proxiedInterfaces); return proxy.newproxyinstance (classloader, proxiedInterfaces, this); } Ensuite, c'est en fait très clair. J'ai déjà écrit les commentaires clairement et je ne les répéterai plus.
La question suivante est que l'objet proxy est généré, comment la surface coupée est-elle tissée?
Nous savons que InvocationHandler est le cœur du proxy dynamique JDK, et les appels de méthode des objets proxy générés seront délégués à la méthode invocationhandler.invoke (). Grâce à la signature de Jdkdynamicaopproxy, nous pouvons voir que cette classe implémente réellement InvocationHandler. Jetons un coup d'œil à la façon dont Spring AOP se trouve dans la section en analysant la méthode invoquée () mise en œuvre dans cette classe.
publicObject invoke (proxy objet, méthode de la méthode, objet [] args) throwsthrowable {méthodinvocation invocation = null; Objet oldproxy = null; booléen setProxyContext = false; Targetsource TargetSource = this.adissed.targetSource; Classe cibleClass = null; Cible objet = null; Try {// EqaulS () Méthode, l'objet cible n'implémente pas cette méthode if (! this.equalsdefined && aoputils.isequalsmethod (méthode)) {return (equals (args [0])? boolean.true: boolean.false); } // HashCode () Méthode, l'objet cible n'implémente pas cette méthode if (! this.hashCodeDefined && aopUtils.ishashCodeMethod (méthode)) {return newInteger (hashCode ()); } // L'interface conseillé ou la méthode définie dans son interface parent, reflète directement l'appel et n'utilise pas la notification si (! This.advised.opaque && méthode.getDeclaringClass (). IsInterface () && méthode.getDeclatingClass (). IsAssignableFrom (conseillère.classe) {// Invocations de service onProxyConfig avec la configuration de la proxy) avec le service Aoputils.InvokeJoinpointUsingReflection (this.adisé, méthode, args); } Objet retval = null; if (this.adissed.exposeproxy) {// rendre l'invocation disponible ifnessary. oldproxy = aopContext.setCurrentProxy (proxy); setProxyContext = true; } // Obtenez la classe cible de l'objet Target = Targetsource.getTarget (); if (cible! = null) {TargetClass = Target.getClass (); } // Obtenez la liste d'interceptor qui peut être appliquée à cette chaîne de méthode chaîne = this.adissed.getInterceptorSandDynamicInterceptionAdvice (méthode, cibleClass); // s'il n'y a pas de notification qui peut être appliquée à cette méthode (intercepteur), cette méthode d'appel de réflexion directe.invoke (cible, args) if (chain.isempty ()) {retval = aoputils.invokejoinpointusingreflection (cible, méthode, args); } else {// Créer Methodinvocation invocation = newReflectIveThodInvocation (proxy, cible, méthode, args, cibleClass, chaîne); retval = invocation.proceed (); } // Valeur de retour de massage si nécessaire. if (retval! = null && retval == Target && méthode.getReturnType (). IsInstance (proxy) &&! RawTargetAccess.class.isassignableFrom (méthode.getDecLaringClass ())) {// Case spécial: il a renvoyé "ce" et le type de retour de la méthode // est le type Compatable. Notant, nous ne pouvons pas aider si la cible définit // une référence à elle-même dans un autre objet retourné. retval = proxy; } return retval; } Enfin {if (Target! = null &&! TargetSource.isstatic ()) {// doit être venu de TargetSource. TargetSource.releasetarget (cible); } if (setProxyContext) {// restaurer l'ancien proxy. AopContext.setCurrentProxy (oldproxy); }}} Le processus principal peut être brièvement décrit comme: l'obtention de la chaîne de notification qui peut être appliquée à cette méthode (chaîne d'intercepteur). S'il y en a, appliquez la notification et exécutez le point de jointure; S'il n'y a pas, reflétez directement le point de jointure. La clé ici est de savoir comment la chaîne de notification est obtenue et comment elle est exécutée. Analyons-le un par un.
Tout d'abord, à partir du code ci-dessus, nous pouvons voir que la chaîne de notification est obtenue via la méthode conseillé.getInterceptorSandDynamicInterceptionAdvice (). Jetons un coup d'œil à la mise en œuvre de cette méthode:
public list <objet> getInterceptorAndDynamicInterceptionAdvice (méthode Method, class TargetClass) {méthodecachekeyycacheKey = new MethodCacheKey (méthode); List <object> cached = this.methodcache.get (cacheKey); if (cached == null) {cached = this.advisorchainfactory.getInterceptorSandDynamicInterceptionAdvice (this, méthode, cibleClass); this.methodcache.put (cachekey, cached); } returncached; } On peut voir que les travaux d'acquisition réels sont réellement effectués par AdvisorChainFactory. GetInterceptorAndDynamicInterceptionAdvice () et les résultats obtenus seront mis en cache.
Analysons la mise en œuvre de cette méthode ci-dessous:
/ ** * Obtenez la liste des conseillers à partir de la configuration de configuration de configuration fournie et traversez ces conseillers. S'il s'agit d'une introduction à l'avis *, déterminez si ce conseiller peut être appliqué à la classe cible TargetClass. S'il s'agit d'un PointCutAdvisor, déterminez * si ce conseiller peut être appliqué à la méthode cible. Le conseiller qui remplit les conditions est converti en une liste d'interceptrice via le conseiller. * / publicList GetInterceptorSandDynamicInterceptionAdvice (Configation conseillé, méthodeMethod, classe TargetClass) {// Ceci est difficile ... Nous devons d'abord traiter les introductions, // Mais nous devons préserver l'ordre dans la liste ultime. List interceptorList = new ArrayList (config.getAdvisors (). Length); // Vérifiez si l'introduction ADVISOR Boolean HasIntroductions = HasmatchingIntroductions (config, cibleClass); // En fait, une série de conseillères AdvisorAdapters est enregistrée ici pour convertir Advisor en MethodInterceptor AdvisorAdapterRegistry Registry = GlobalAdVisorAdapterRegistry.getInstance (); Advisor [] Advisors = config.getAdvisors (); pour (int i = 0; i <consorant.length; i ++) {Advisor Advisor = Advisors [i]; if (Advisor instanceof PointCutAdVisor) {// Ajoutez-le conditionnellement. PointCutAdvisor PointCutAdvisor = (PointCutAdvisor) Advisor; if (config.isprefiltered () || PointCutadVisor.getPointcut (). GetClassFilter (). Matches (TargetClass)) {// TODO: Les positions de ces deux méthodes peuvent être échangées dans ce lieu // Convert Advisor en Interceptor MethodInterceptor [] interceptors = registre.getInterceptor (Advisor); // Vérifiez si le point de point du conseiller actuel peut correspondre à la méthode actuelle MethodMatcher mm = PointCutAdvisor.getPointcut (). GetMethodMatcher (); if (methodmatchers.matches (mm, méthode, targetClass, HasIntroductions)) {if (mm.isruntime ()) {// Création d'une instance newObject dans la méthode getInterceptor () // n'est pas un problème, nous cache normalement des chaînes créées. for (intj = 0; j <interceptors.length; j ++) {interceptorList.add (new interceptorandnamicMethodMatcher (intercepteurs [j], mm)); }} else {interceptorList.addall (arrays.aslist (intercepteurs)); }}}} else if (Advisor instanceof IntroductionAdvisor) {IntroductionAdvisor ia = (IntroductionAdvisor) Advisor; if (config.isprefiltered () || ia.getClassFilter (). Matches (TargetClass)) {interceptor [] interceptors = registry.getInterceptors (conseiller); interceptorlist.addall (arrays.aslist (intercepteurs)); }} else {interceptor [] interceptors = registry.getInterceptors (conseiller); interceptorlist.addall (arrays.aslist (intercepteurs)); }} return interceptorlist; } Une fois cette méthode exécutée, tous les conseillers configurés dans conseillé qui peuvent être appliqués aux points de connexion ou aux classes cibles sont convertis en méthode Interceptor.
Ensuite, examinons comment fonctionne la chaîne intercepteur obtenue.
if (chain.isempty ()) {retval = aopUtils.InvokeJoinSpointUsingReflection (cible, méthode, args); } else {// Créer Methodinvocation invocation = newReflectIveThodInvocation (proxy, cible, méthode, args, cibleClass, chaîne); retval = invocation.proceed (); } À partir de ce code, nous pouvons voir que si la chaîne d'intercepteur obtenue est vide, la méthode cible sera appelée directement reflétée. Sinon, une méthodeinvocation sera créée, sa méthode de processus sera appelée et l'exécution de la chaîne d'intercepteur sera déclenchée. Jetons un coup d'œil au code spécifique
Public Object Proced () lève le lancement {// nous commençons par un indice de -1 et l'incrément tôt. if (this.currentInterceptorIndex == this.InterceptorAndDynamicMethodMatchers.size () - 1) {// Si l'interceptor est fini, exécuter, exécuter joinpoint return invokejoinpoint (); } Objet interceptororinterceptAdvice = this.InterceptorAndDynamicMethodMatchers.get (++ this.currentInterceptorIndex); // Si vous souhaitez faire correspondre dynamiquement JoinPoint if (InterceptororInterceptAdvice Instance d'interceptoranddynamicMethodmatcher) {// Évaluer le match de méthode dynamique ici: la pièce statique aura déjà // a été évaluée et jugée correspondant. InterceptorandDynamicMethodmatcher DM = (interceptoranddynamicMethodmatcher) interceptororinterceptAdvice; // Match dynamique: si les paramètres d'exécution remplissent les conditions de correspondance if (dm.Methodmatcher.matches (this.method, this.targetClass, this.arguments)) {// exécuter le returndm.Interceptor.invoke (this) actuel actuel; } else {// Lorsque la correspondance dynamique échoue, ignorez l'intercette actuelle et appelez la prochaine procédure de retour interceptor (); }} else {// C'est un intercepteur, donc nous l'invitons simplement: le point de point a // a été évalué statiquement avant la construction de cet objet. // Exécuter l'intercette return actuel ((MethodInterceptor) interceptororinterceptionAdvice) .Invoke (this); }}Le code est également relativement simple, donc je n'entrerai pas dans les détails ici.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.