Préface
Après l'article précédent Spring Decryption - analyse XML et enregistrement des haricots, continuons à analyser le code source. Sans plus tarder, jetons un coup d'œil à l'introduction détaillée ensemble.
Décryptage
Il existe deux principales catégories de déclarations dans la configuration XML de Spring. L'un est celui par défaut, tel que <bean id="person"/> , et l'autre est celui personnalisé, tel que <tx:annotation-driven /> . Les deux balises ont des méthodes d'analyse très différentes. La méthode paramètres de la définition est utilisée pour distinguer les méthodes d'analyse utilisées par différentes balises. Obtenez l'espace de noms via node.getNamespaceURI() , déterminez s'il s'agit d'un espace de noms par défaut ou d'un espace de noms personnalisé, et comparez-le avec l'espace de noms fixe http://www.springframework.org/schema/beans à Spring. S'il est cohérent, utilisez parseDefaultElement(ele, delegate); Sinon, il s'agit delegate.parseCustomElement(ele);
Analyse des balises par défaut
ParseDefaultElement a effectué un traitement différent pour 4 étiquettes différentes importation, alias, haricots et haricots. Parmi eux, l'analyse des étiquettes de haricots est la plus complexe et la plus importante, nous allons donc commencer une analyse approfondie du haricot. Si nous pouvons comprendre le processus d'analyse de cette balise, l'analyse d'autres balises sera naturellement résolue. Dans l'article précédent, nous le décrivons brièvement. Dans cet article, nous en discuterons en détail autour du module d'analyse.
classe publique DefaultBeAnDefinitionDocumentReader implémente BeanDefinitionDocumentReader {private void parsedefaultElement (élément ele, beanDefinitionParserDelegate Delegate) {// import tag analyseing if (Delegate.NodeNameequals (ele, import_element)) {importbeandeFinIntionResource (ele); } // Alias TAG analyse else if (delegate.NodeNameequals (ele, alias_element)) {ProcessaliasRegistration (ele); } // Résolution de balise bean else if (delegate.nodeNameequals (ele, bean_element)) {processBeAndeFinition (ele, délégué); } // Importer une résolution de balise else if (delegate.nodeNameequals (ele, nested_beans_element)) {// la méthode récursive de résolution de balise doregisterBeAnDefinitions (ele); }}} Tout d'abord, analysons processBeanDefinition(ele, delegate) dans la classe
Procédure de processus VOIDADefinition (Element Ele, délégué de la relèvement de la cloison) {// de la méthode ParseBeAndefinitionment de la classe BeanDefinitionDelegate pour la analyse des éléments (Ele); if (bdHolder! = null) {// Lorsque le titulaire de BD retourné n'est pas vide, s'il y a un attribut personnalisé sous le nœud enfant de l'étiquette par défaut, vous devez analyser à nouveau l'étiquette personnalisée BDHolder = Delegate.DecorateBeAnDefinitionIiFrequired (ele, bdolder); Essayez {// une fois l'analyse terminée, le titulaire d'analyse doit être enregistré. L'opération d'enregistrement est déléguée à la méthode RegisterBeanDefinition de BeanDefinitionReaderUtils.RegisterBeAnDefinition (BDHolder, getReaderContext (). GetRegistry ()); } catch (beanDefinitionStoreException ex) {getReaderConText (). Error ("Échec de l'enregistrement de la définition du bean avec le nom '" + bdHolder.getBeanName () + "'", ele, ex); } // Enfin, un événement de réponse est publié pour informer l'auditeur pertinent que le bean a été chargé getReaderContext (). FiReconentRegistered (new BeanComponentDefinition (BDHolder)); }}Dans ce code:
Analysons en détail comment le printemps analyse chaque balise et nœud
Analyse de la balise de haricot
Classe publique BeanDefinitionParserDelegate {@Nullable public BeanDefinitionHolder ParseBeBeAndeFinitionElement (Element ele, @Nullable BeanDefinition ContintainBean) {// Obtenez l'attribut ID de la chaîne de bean tag id = ele.getAttribute (id_Attribute); // Obtenez l'attribut de nom de la chaîne de balise bean nameattr = ele.getAttribute (name_attribute); List <string> aliases = new ArrayList <> (); if (stringUtils.hasLength (nameattr)) {// transmettez la valeur de l'attribut de nom, et divisez-le en un numéro de chaîne (c'est-à-dire que si plusieurs noms sont configurés dans le fichier de configuration, traitez-le ici) string [] namearr = stringUtils.TokenizetOstringArray (nameAtrTr, multi_value_attribut_delimiters); aliases.addall (arrays.aslist (namearr)); } String beanname = id; // Si l'ID est vide, utilisez l'attribut de prénom configuré comme id if (! StringUtils.hastext (beanname) &&! Aliases.isempty ()) {beanname = aliases.reMove (0); if (logger.isdebugeNable ()) {logger.debug ("no xml 'id' spécifié - en utilisant '" + beanname + "' as bean name et" + aliases + "as aliases"); }} if (contenantBean == NULL) {// Vérifiez l'unicité de Beanname et Aliases // Le noyau interne consiste à utiliser la collection de noms d'occasion pour sauver tous les noms de soutenance et l'aliasesupidité utilisés (feanname, aliases, ele); } // analyser davantage toutes les autres propriétés dans l'objet générique de la définition AbstractBeanDefinition BeanDefinition = ParseBeAnDefinitionElement (Ele, Beanname, contenueBean); if (beanDefinition! = null) {// Si le bean ne spécifie pas le nom de haricot, utilisez la règle par défaut pour générer du nom de haricot pour ce bean if (! stringUtils.hastext (beanname)) {try {if (contenantBean! = null) {beanDame = BeanDefinitionRereRITH vrai); } else {beanname = this.readerConText.GenEREReBeAnName (beanDefinition); // Enregistrez un alias pour le nom de classe de bean ordinaire, s'il est toujours possible, // si le générateur a renvoyé le nom de classe plus un suffixe. // Ceci est attendu pour la compatibilité des printemps 1.2 / 2.0 vers l'arrière. String beanclassName = beanDefinition.getBeANClassName (); if (beanclassName! = null && beanname.startswith (beanclassname) && beanname.length ()> beanclassname.length () &&! this.readerConText.getRegistry (). IsBeanNameInUse (beanclassname)) {aliases.add (beanclassname); }} if (logger.isdebugeNable ()) {logger.debug ("ni xml 'id' ni 'name' spécifié -" + "Utilisation du nom de bean généré [" + beanname + "]"); }} catch (exception ex) {error (ex.getMessage (), ele); retourner null; }} String [] aliasesArray = stringUtils.toStringArray (aliases); // encapsulent les informations dans l'objet BeanDefinition RETOUR RETOUR le nouveau HEAGDEFINITIONHY (BeanDefinition, Beanname, aliasesArray); } return null; }} Cette méthode traite principalement des attributs connexes tels que l'ID, le nom, l'alias, etc., génère du nom de beanname et complète l'analyse de la balise de base dans la méthode de la fonction de surcharge par la fonction parseBeanDefinitionElement(ele, beanName, containingBean) .
Ensuite, concentrez-vous sur parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)
Voir comment il termine l'opération d'analyse de tag
Nœud de haricot et analyse d'attribut
@NullablePublic AbstractBeBeAndefinition ParseBeAnDefinitionElement (élément ele, string beanname, @Nullable BeanDefinition contientBean) {this.parsestate.push (new Beanentry (beanname)); // Obtenez l'attribut de classe du bean tag string className = null; if (ele.hasattribute (class_attribute)) {className = ele.getAttribute (class_attribute) .trim (); } // Obtenez l'attribut parent de la chaîne de tag bean Parent = null; if (ele.hasattribute (parent_attribute)) {parent = ele.getAttribute (parent_attribute); } Essayez {// Créer AbstractBeAnDefinition aux attributs d'hôte AbstractBeAndeFinition bd = CreateBeAnDefinition (ClassName, Parent); // obtient divers attributs de la balise de haricot ParseBeAnDefinitionAtTributes (Ele, beanname, contenantBean, BD); // Parse Description TAG BD.SetDescription (domutils.getChildElementValueByTagName (ele, description_element)); // analyse Meta Tag Parsemetaelements (Ele, BD); // analyse de recherche de recherche de recherche PARSELOOKUPOVERRIDESUBELlements (ele, bd.getMethodoverrides ()); // analyse de la tag de méthode remplacé ParsereplacedMethodSubElements (ele, bd.getMethodoverrides ()); // analyse de la tag de méthode remplacé ParsereplacedMethodSubElements (ele, bd.getMethodoverrides ()); // analyse du constructeur-arg de la balise PARSECONSTRUCTURCELlements (ELE, BD); // Parse Property Tag paSepropertyElements (ele, bd); // analyse de qualification de l'analyse parasesalifielements (ele, bd); bd.setResource (this.readerContext.getResource ()); BD.SetSource (ExtractSource (ELE)); retour bd; } catch (classNotFoundException ex) {error ("bean class [" + classname + "] non trouvé", ele, ex); } catch (noclassdeffoundError err) {error ("class that bean class [" + classname + "] dépend de non trouvé", ele, err); } catch (ex) {error ("défaillance inattendue pendant l'analyse de définition du bean", ele, ex); } enfin {this.parsestate.pop (); } retourner null;}Analyser davantage d'autres attributs et éléments (il existe de nombreux éléments et attributs, il s'agit donc d'une énorme charge de travail) et de les emballer en générique de définition. Après l'analyse de ces attributs et éléments, si vous détectez que le haricot n'a pas de nom de haricot spécifié, vous utilisez les règles par défaut pour générer un nom de haricot pour le bean.
// BeanDefinitionParserDelegate.Javaprotected AbstractBeAnDefinition CreateBeAndeFinition (@Nullable String classname, @Nullable String Parentname) lève ClassNotFoundException {return beandeFinitionReDenerUtils.createBeanDeFinition (ParentName, ClassicAl Class BeanDefinitionReaderUtils {public static abstractBeAndefinition CreateBeAndeFinition (@Nullable String parentName, @Nullable String classname, @Nullable classloader classloader) lève ClassNotFoundException {généricbeandefinition bd = new GenericBeAnDefinition (); // Le nom parent peut être vide bd.setparentName (nom parent); // Si Classloader n'est pas vide //, utilisez le Classloader passé pour charger l'objet de classe avec la même machine virtuelle. Sinon, seul classloader est enregistré si (className! = Null) {if (classloader! = Null) {bd.setBeANClass (classutils.forname (classname, classloader)); } else {bd.setBeANClassName (className); }} return bd; }}BeanDefinition est une représentation interne de <an Bean> dans un récipient, et BeanDefinition et <Ean> sont un à un. Dans le même temps, BeanDefinition sera enregistré dans BeanDefinitionRegistry, qui est comme la base de données en mémoire des informations de configuration de ressort.
Jusqu'à présent, createBeanDefinition(className, parent); a été terminé, et nous avons également obtenu le résumé de la définition utilisée pour héberger les attributs. Jetons un coup d'œil à la façon dont parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); ParseBeAndefinitionAttributes (Ele, Name, contenant BD, BD); analyser divers attributs de balises dans les haricots.
Classe publique BeanDefinitionParserDelegate {public AbstractBeAndefinition ParseBeBeAndefinitionAtTributes (Element Ele, String Beanname, @Nullable BeanDefinition contient le code détaillé, cette partie du code détermine principalement si elle contient l'attribut spécifié par ailleurs. S'il y a, bd.set (attribut); retour bd; }} ``L'analyse complète de la balise «Bean» est terminée. L'élément analysant sous la balise «Bean» est similaire. Si vous êtes intéressé, vous pouvez suivre le code source et voir les méthodes d'analyse telles que «qualificatif, recherche-méthode» (* non compliqué que «Bean» *). Le contenu de la balise personnalisée est plus détaillé dans le chapitre suivant.
Enfin, encapsuler les informations obtenues dans l'instance de «BeanDefinitionholder»
`` `java // beandefinitionParserdelegate.java@nullablepublic beandefinitionholder parsebeAndefinitionElement (element ele, @nullable beandefinition contientbean) {// ... return beandefinitionholder (beandefinition, beanname, aliasesArray);};Enregistrez un BeanDefinition analysé
Après analyser le fichier de configuration, nous avons obtenu toutes les propriétés du haricot, et l'étape suivante consiste à enregistrer le haricot
Classe publique BeanDefinitionReaderUtils {public static void RegisterBeanDefinition (BeanDefinitionholder Definitionholder, BeanDefinition Registry) lance BeanDefinitionStoreException {// Utiliser Beanname comme identifiant unique String STRAND = DÉFINICHOLDER.GETBEAnNAME (); // Enregistrez le code de base du Bean Registry.RegisterBeAndeFinition (BeanName, DefinitionHolder.getBeAnDefinition ()); // Enregistrer tous les alias pour la chaîne de bean [] alias = de définitionholder.getaliases (); if (aliases! = null) {for (String alias: aliases) {registry.registeralias (beanname, alias); }}}}Le code ci-dessus complète principalement deux fonctions: l'une consiste à enregistrer BeanDefinition à l'aide de beanname, et l'autre est de terminer l'enregistrement d'alias.
RECORD DE PEANNAME BEANDEFINITION
classe publique defaultListableBeAnfactory {@Override public void registerBeAndefinition (string beanname, beanDefinition BeanDefinition) lève BeanDefinitionStoreException {assert.hastext (beanname, "le nom de bean ne doit pas être vide"); Affirmer.notnull (BeanDefinition, "BeanDefinition ne doit pas être nul"); if (beandefinition instance of AbstractBeAnDefinition) {try {// La dernière vérification avant l'enregistrement, la vérification ici est différente de la vérification du fichier XML // Il s'agit principalement de la vérification de la méthode Overrides dans la méthode abstraite de la méthode nette BeanDefinition) .validate (); } catch (beanDefinitionValidationException ex) {lancez new BeanDefinitionStoreException (beanDefinition.getResourceDeScription (), beanname, "La validation de la définition du bean a échoué", ex); }} BeanDefinition oldBeAnDefinition; // Obtenez le BeanDefinition dans le cache oldBeAndefinition = this.beAndefinitionMap.get (beanname); if (oldBeAndefinition! = null) {// s'il y a un cache, déterminez si l'écrasement est autorisé si (! IsallowBeAndeFinitionOverrid ()) {New BeanDefinitionStoreException (BeanDefinition.GetResourceDeScription (), Beanname, "Impossible d'enregistrer la définition de Bean [" + BeanDefinition + " OldBeAndefinition + "] Bound."); } else if (oldbeandefinition.getrole () <BeanDefinition.getRole ()) {// par exemple était Role_Application, désormais remplacé par Role_Support ou Role_infrastructure if (this.logger.iswarneNabled ()) {this.logger.warn ("Overring Usered Defined Definice for bee Définition: Remplacement ["+ oldBeAnDefinition +"] par ["+ BeanDefinition +"] "); }} else if (! beanDefinition.equals (oldBeAndefinition)) {if (this.logger.isinfoenabled ()) {this.logger.info ("Définition de bean pour le bean '" + beanname + "' avec une définition différente: remplacer [" + oldbeandefinition + "] par [" + beanDefinition + "]"); }} else {if (this.logger.isdebugeNabled ()) {this.logger.debug ("Définition de bean overring pour bean '" + beanname + "' avec une définition équivalente: remplacer [" + oldbeandefinition + "] par [" + hEAGDEFINITION + "]"); }} // Si l'écrasement est autorisé, enregistrez BeanDefinition dans BeanDefinitionMap this.beAndefinitionMap.put (beanname, beanDefinition); } else {// Déterminez si la création de bean a commencé if (HasbeanCreationStarted ()) {// ne peut plus modifier les éléments de collecte de démarrage (pour une itération stable) synchronisée (this.beAndefinitionMap) {// Save BeanDefinition dans BeanDefinitionMap This.BeAndefinitionMap.put (Beanname, BeanDefinition); // Mettre à jour la liste de noms de nom inscrits <string> UpdatedDefinitions = new ArrayList <> (this.beAndeFinitionNames.size () + 1); UpdatedDefinitions.Addall (this.BeAndeFinitionNames); UpdatedDefinitions.Add (beanname); this.BeAndeFinitionNames = UpdatedDefinitions; if (this.ManualSingLetOnNames.Contains (beanname)) {set <string> updatedSingletons = new LinkedHashSet <> (this.ManualSingLeTonNames); UpdatedSingletons.Remove (beanname); this.ManualSingLetOnNames = UpdatedSingletOn; }}} else {// Je n'ai pas encore commencé à créer des haricots. this.beAndefinitionNames.add (beanname); this.manualsingletonames.remove (beanname); } this.frozenBeAndeFinitionNames = null; } if (oldBeAnDefinition! = null || ContiseSSingleton (beanname)) {// réinitialiser le cache correspondant à la nom de resetBeAnDefinition (beanname); }}}Enregistrer un alias
Après avoir enregistré BeanDefinition, l'étape suivante consiste à enregistrer un alias. La relation correspondante entre l'alias enregistré et le nom de haricot est stockée dans l'aliasmap. Vous constaterez que la méthode RegisterAlias est implémentée dans SimpLealiasRegistry
classe publique SimplaLeIasRegistry {/ ** Map de l'alias au nom canonique * / carte finale privée <String, String> aliasmap = new ConcurrentHashMap <> (16); public void registeralias (String name, String alias) {assert.hastext (name, "'name' ne doit pas être vide"); Assert.hastext (alias, "'alias' ne doit pas être vide"); if (aliaS.equals (name)) {// Si le nom de haricot est le même que l'alias, l'alias ne sera pas enregistré et supprimera l'alias correspondant this.aliasmap.remove (alias); } else {String regreedName = this.aliasmap.get (alias); if (registredName! = null) {if (registredName.equals (name)) {// si l'alias a été enregistré et le nom indiqué est le même que le nom actuel, aucun traitement ne sera effectué; } // Si Alias n'autorise pas l'écrasement, une exception sera lancée si (! AllowaliaSoVerriding ()) {Throw New illégalStateException ("Impossible d'enregistrer Alias '" + alias + "' pour le nom '" + name + "': il est déjà enregistré pour le nom '" + enregistré + "'."); }} // La boucle de vérification pointe vers des dépendances telles que a-> b b-> c c-> a, une erreur se produit CheckForaliasCircle (nom, alias); this.aliasmap.put (alias, nom); }}} Vérifiez la dépendance circulaire d'alias via la méthode checkForAliasCircle() . Lorsqu'un -> b existe, si un -> c -> b réapparaît, une exception sera lancée:
Protected void checkForaliasCircle (String Name, String alias) {if (Hasalias (alias, name)) {Throw New illégalStateException ("Impossible d'alias '" + alias + "' pour le nom '" + name + "': référence circulaire - '" + name + "' est un alias direct ou indirect pour '" + Alias + "' 'déjà"); }} public boolean hasalias (String Name, String alias) {for (map.entry <string, string> entry: this.aliasmap.entryset ()) {string regreedName = entry.getValue (); if (registredName.equals (name)) {String registeralias = entry.getKey (); Retour (registredalis.equals (alias) || Hasalias (registreedalias, alias)); }} return false;}À ce stade, l'enregistrement d'alias a été achevé et les tâches suivantes ont été effectuées.
Envoyer une notification
Informer l'auditeur à analyser et à enregistrer
//DefaultBeAnDefinitionDocumentReader.Javaprotected void ProcessBeanDefinition (Element Ele, BeanDefinitionParserDelegate Delegate) {// Envoi de l'événement d'enregistrement. getReaderConText (). FireComponentRegistered (new BeanComponentDefinition (BDHolder));}La méthode récompensée par FireComponent est utilisée pour informer l'auditeur pour analyser et enregistrer les travaux. L'implémentation ici est uniquement pour l'extension. Lorsque le développeur du programme doit écouter l'événement BeanDefinition enregistré, il peut enregistrer l'auditeur et écrire la logique de traitement dans l'auditeur. Actuellement, le printemps ne gère pas cet événement dans cet événement
Le ReaderContext est généré en appelant CreateRenerConText dans la classe XMLBeAndeFinitionReader, puis en appelant fireComponentRegistered()
Analyse de l'étiquette d'alias
Spring fournit une configuration d'alias pour <alias name="person" alias="p"/> . La résolution de balises est effectuée dans la méthode de procédaliasRatisation (Element ELE).
classe publique defaultBeAndeFinitionDocumentReader {Protected void processAliaSRegistration (élément ele) {// get ALISA TAG Name Property String name = ele.getAttribute (name_attribute); // Get Alisa Tag Alisa Tag Alias Attribute String alias = ele.getAttribute (alias_attribute); booléen valide = true; if (! stringUtils.hastext (name)) {getReaderContext (). Error ("le nom ne doit pas être vide", ele); valide = false; } if (! stringUtils.hastext (alias)) {getReaderConText (). Error ("alias ne doit pas être vide", ele); valide = false; } if (valide) {try {// registre alias getReaderContext (). getRegistry (). registeralias (name, alias); } catch (exception ex) {getReaderConText (). Error ("Échec de l'enregistrement d'alias '" + alias + "' pour bean avec le nom '" + name + "'", ele, ex); } // Après avoir enregistré l'alias, dites à l'auditeur de faire le traitement correspondant getReaderContext (). FiRealiasRegistered (nom, alias, extraitSource (ele)); }}}Tout d'abord, l'attribut de balise d'alias est extrait et vérifié. Une fois la vérification adoptée, l'enregistrement d'alias est effectué. L'enregistrement des alias et l'enregistrement des alias dans l'analyse des étiquettes de haricots ont été effectués. Je ne le répéterai pas ici.
Analyse des balises d'importation
classe publique defaultBeAndeFinitionDocumentReader {Protected void importBeAndeFinIitionResource (Element ele) {// Obtenez l'attribut de ressource de la chaîne d'importation location = ele.getAttribute (ressource_attribute); // s'il n'existe pas, aucun traitement n'est effectué si (! StringUtils.hastext (emplacement)) {getReaderConText (). Erreur ("l'emplacement de la ressource ne doit pas être vide", ele); retour; } // Format d'attribut d'attribut de placement d'analyse tel que "$ {user.dir}" emplacement = getReaderConText (). GETENVERNAMENT (). ResolverEquiredPlaceHolders (emplacement); Set <Resource> réelResources = new LinkedHashSet <> (4); // Déterminez si la ressource est un chemin absolu ou un chemin relatif booléen Absolutelocation = false; try {AbsoluteLocation = ResourcePatterNutils.isurl (emplacement) || ResourceUtils.touri (emplacement) .isabsolute (); } catch (urisyntaxException ex) {// ne peut pas se convertir en URI, considérant l'emplacement relatif // à moins qu'il ne s'agisse du préfixe de ressort bien connu "CLASSPATH *:"} // s'il s'agit d'un chemin absolu, le fichier de configuration correspondant sera chargé directement selon l'adresse si (absolueLocat) {try {int importCount = getReaderConText (). getReader (). LoadBeAndefinitions (emplacement, réelResources); if (logger.isdebugeNabled ()) {logger.debug ("importée" + importance + "définitions de bean à partir de l'emplacement de l'URL [" + emplacement + "]"); }} catch (beanDefinitionStoreException ex) {getReaderConText (). Error ("Échec de l'importation de définitions de bean à partir de l'emplacement de l'URL [" + emplacement + "]", ele, ex); }} else {try {int importance; // Chargez la ressource en fonction de la ressource de chemin relative relativeResource = getReaderContext (). GetResource (). CreateRelative (emplacement); if (RelaTIVERSOURCE.Exists ()) {importCount = getReaderConText (). getReader (). LoadBeAnDefinitions (relativerResource); réelResources.add (relativerResource); } else {String BasElocation = GetReaderConText (). getResource (). getUrl (). toString (); ImportCount = getReaderConText (). getReader (). LoadBeAndefinitions (stringUtils.ApplyrelativePath (BasElocation, emplacement), réelResources); } if (logger.isdebugeNabled ()) {logger.debug ("importée" + importance + "Définitions de bean à partir de l'emplacement relatif [" + emplacement + "]"); }} catch (ioException ex) {getReaderConText (). Error ("Échec de la résolution de l'emplacement actuel des ressources", ele, ex); } catch (beanDefinitionStoreException ex) {getReaderContext (). Error ("Échec de l'importation de définitions de bean à partir de l'emplacement relatif [" + emplacement + "]", ele, ex); }} // Après l'analyse, le traitement d'activation de l'auditeur est effectué. Ressource [] actreSArray = réelResources.toArray (nouvelle ressource [réelResources.size ()]); getReaderConText (). FireImportProcessed (emplacement, actresArray, ExtractSource (ELE)); }} Après avoir terminé le traitement de la balise d'importation, la première chose est d'obtenir le chemin représenté par l'attribut de <import resource="beans.xml"/> , puis analyser l'espace réservé à l'attribut dans le chemin tel que ${user.dir} , puis déterminer si l'emplacement est un chemin absolu ou un chemin relatif. S'il s'agit d'un chemin absolu, le processus d'analyse du haricot est appelé récursivement (loadBeanDefinitions(location, actualResources);) pour effectuer une autre analyse. S'il s'agit d'un chemin relatif, calculez le chemin absolu et analysez-le. Enfin, aviser l'auditeur et l'analyse est terminée.
Résumer
Après quelques personnes, hiver, printemps et été inconnus, tout suivra la direction que vous voulez ...
D'accord, ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.
Dire quelque chose
Code texte complet: https://gitee.com/battcn/battcn-spring-source/tree/master/chapter1 (téléchargement local)