Prototype Spring IOC
Le noyau de base et le point de départ du cadre de ressort sont sans aucun doute IOC. En tant que technologie de base fournie par les conteneurs Spring, le CIO a réussi à compléter l'inversion des dépendances: de la gestion active de la classe principale sur les dépendances au contrôle global des dépendances par les conteneurs de printemps.
Quels sont les avantages de faire cela?
Bien sûr, c'est le soi-disant "découplage", qui peut rendre la relation entre les modules du programme plus indépendante. Spring doit seulement contrôler les dépendances entre ces modules et créer, gérer et maintenir ces modules en fonction de ces dépendances pendant le processus de démarrage et d'initialisation du conteneur. Si vous devez modifier les dépendances entre les modules, vous n'avez même pas besoin de modifier le code du programme. Il vous suffit de modifier les dépendances modifiées. Spring rétablira ces nouvelles dépendances dans le processus de recommandation et d'initialisation du conteneur. Dans ce processus, il est nécessaire de noter que le code lui-même n'a pas besoin de refléter la déclaration de la situation de dépendance spécifique du module mais n'a besoin que de définir l'interface du module requis. Par conséquent, il s'agit d'une idée typique orientée vers l'interface. Dans le même temps, il est préférable d'exprimer les dépendances sous forme de fichiers de configuration ou d'annotations. Les classes de traitement Spring pertinentes assembleront les modules en fonction de ces fichiers de configuration externes, ou scanneront l'annotation pour appeler le processeur d'annotation interne pour assembler des modules pour terminer le processus IOC.
Le but du CIO est une injection de dépendance appelée DI. Grâce à la technologie du CIO, le conteneur ultime nous aidera à terminer l'injection de dépendance entre les modules.
De plus, le dernier point est que dans le processus de Spring IOC, nous devons toujours être clairs sur la ligne principale ci-dessus. Peu importe à quel point la syntaxe et la structure de classe en temps réel sont complexes, sa fonction et son objectif sont les mêmes: il s'agit de compléter "l'assemblage" du module en s'appuyant sur le fichier de configuration décrit comme le "dessin" de l'assemblage. La syntaxe complexe n'est qu'un moyen d'atteindre cet objectif.
Le soi-disant prototype IOC, afin de montrer le schéma du SIO le plus simple, nous pourrions aussi bien faire un prototype complètement simple pour illustrer ce processus:
Tout d'abord, il existe plusieurs modules que nous définissons, y compris le module principal et le module de dépendance défini par les deux interfaces:
classe MAINMODULE {private dépendModulea modulea; MODULEB DE DÉSENDUDEB PRIVÉB; public dépendModulea getmodulea () {return modulea; } public void setModulea (dépendModulea modulea) {this.modulea = modulea; } public dépendModuleB getModuleB () {return moduleB; } public void setModuleB (DependModuleB moduleB) {this.moduleB = moduleB; }} Interface dépendModulea {public void funcfrommodulea ();} interface dépendModuleB {public void funcfromuleb ();} classe dépendModuleAIMPl implémente dépendModulea {@Override public void funcfrommodulea () {System.out.println ("Il s'agit du Func de module de module a"); }} classe DependModuleBimplt implémente DependModuleB {@Override public void funcfromuleb () {System.out.println ("Ceci est func du module b"); }}Si nous n'adoptons pas le CIO, mais que nous comptons sur le module principal lui-même pour contrôler la création de son module dépendant, alors ce sera comme ceci:
classe publique SimpleIOCDemo {public static void main (String [] args) lève classNotFoundException {mainmodule MainModule = new MainModule (); mainmodule.setModulea (new DependModuleAIMPl ()); mainmodule.setModuleB (new DependModuleBimpl ()); mainmodule.getModulea (). Funcfrommodulea (); mainmodule.getModuleB (). FuncFromuleB (); }}Il s'agit de notre définition simplifiée du prototype de conteneur IOC. Lorsque le conteneur est initialisé après le démarrage, il lira le fichier de configuration écrit par l'utilisateur. Ici, nous prenons le fichier de configuration des propriétés simples à titre d'exemple. Ce n'est que lorsque l'utilisateur appelle la méthode GetBean, le bean correspondant sera vraiment assemblé et chargé en fonction du fichier de configuration. Une carte utilisée pour enregistrer le haricot assemblé est maintenue à l'intérieur du prototype de conteneur que nous avons défini. S'il y a un haricot qui répond aux exigences, il n'a plus besoin d'être créé:
classe SimpleIOCContainer {Private Properties Properties = New Properties (); map privé <chaîne, objet> moduleMap = new HashMap <> (); ? } catch (exception e) {e.printStackTrace (); }} Objet public getBean (String moduleName) lève ClassNotFoundException {objet instanceObj; if (modulemap.get (moduleName)! = null) {System.out.println ("return old bean"); return modulemap.get (moduleName); } System.out.println ("Créer un nouveau bean"); String fullClassName = Properties.getProperty (moduleName); if (fullClassName == null) lancez new classNotFoundException (); else {class <? Étend Object> Clazz = class.Forname (FullClassName); essayez {instanceObj = Clazz.NewInstance (); instanceObj = buildAtTachedModules (moduleName, instanceObj); modulemap.put (modulename, instanceObj); return instanceObj; } catch (InstantiationException e) {e.printStackTrace (); } catch (illégalaccessException e) {e.printStackTrace (); }} return null; } objet privé buildAttachedModules (String moduleName, objet instanceObj) {set <string> PropertiesKeys = Properties.StringPropertyNames (); Champ [] champs = instanceObj.getClass (). GetDeclaredFields (); for (String key: PropertiesKeys) {if (key.contains (moduleName) &&! key.equals (modulename)) {try {class <? Étend Object> Clazz = class.Forname (Properties.GetProperty (Properties.GetProperty (KEY))); pour (champ de champ: champs) {if (field.getType (). IsAssignableFrom (Clazz)) field.set (instanceObj, Clazz.newinstance ()); }} catch (exception e) {e.printStackTrace (); }}} return instanceObj; }}Il s'agit du fichier de configuration de dépendance que nous avons écrit à l'aide du fichier de configuration des propriétés. Ce fichier de configuration est le "dessin" de notre module d'assemblage. La syntaxe ici est complètement définie par nous. Dans le réel conteneur IOC Spring, afin d'exprimer une logique de dépendance plus complexe, un fichier de configuration de format XML plus développé ou une nouvelle configuration d'annotation sera utilisé, et le processeur d'annotation sera utilisé pour terminer l'analyse du dessin:
mainmodule = com.rocking.demo.mainmodulemainmodule.modulea = moduleamainmodule.moduleB = moduleBModulea = com.rocking.demo.dependModuleAilLe
Ceci est le code de test. On peut voir que nous pouvons obtenir complètement des modules qui répondent aux exigences via le conteneur IOC que nous avons défini. En même temps, nous pouvons également constater que le récipient que nous avons défini peut maintenir ces haricots pour nous. Lorsqu'un haricot a été assemblé et créé, il n'a plus besoin d'être créé.
classe publique SimpleIOCDemo {public static void main (String [] args) lève classNotFoundException {SimpleIOcContainer Container = new SimpleIOCContainer (); DépendModulea modulea = (dépendModulea) contener.getBean ("modulea"); modulea.funcfrommodulea (); DependModuleB moduleB = (dépendModuleB) contener.getBean ("moduleB"); moduleB.funcFromuleB (); MAINMODULE MAINMODULE = (MainModule) Container.getBean ("MainModule"); mainmodule.getModulea (). Funcfrommodulea (); mainmodule.getModuleB (). FuncFromuleB (); contener.getBean ("MainModule"); }}Ceci est le prototype de conteneur IOC que j'ai créé sur la base de l'idée de base du CIO. Bien que Spring IOC ait une syntaxe complexe, les tâches effectuées à la fin sont les mêmes au cœur, ce que l'on appelle "tous les changements ne seront pas séparés de leur essence".
Processus spécifique du Spring IOC
La dernière fois, le prototype de l'implémentation générale du CIO a été montré. Alors, comment implémenter spécifiquement le processus de chargement des POJOS dans le cadre printanier de ce conteneur en fonction de la configuration des méta-informations des métadonnées? Il existe de nombreux endroits dans l'ensemble du processus de travail du conteneur du Spring IOC conçu pour être assez flexible, offrant aux utilisateurs beaucoup d'espace pour effectuer leurs propres tâches, plutôt que de simplement compléter le processus mécanique du conteneur.
Il s'agit du diagramme de processus de l'ensemble du processus de travail du conteneur IOC:
1. Étape de démarrage du conteneur (1) Chargement des informations sur le fichier de configuration (2) Analyser les informations de fichier de configuration (3) Assemblage BeanDefinition
(4) Le post-traitement d'abord, les méta-informations telles que les fichiers de configuration ou les annotations et les informations de classe Javabean sont chargées dans le conteneur IOC. Le conteneur lit un fichier de configuration XML-format. Ce fichier de configuration est une dépendance déclarée par l'utilisateur et l'assemblage qui nécessite une attention particulière. Il s'agit d'un «dessin externe» précoce pour assembler le haricot. Le moteur d'analyse dans le conteneur peut analyser la méta-information du caractère sous la forme de texte que nous écrivons dans une hEAGDEFINITION qui peut être reconnue à l'intérieur du récipient, qui peut comprendre le BeanDefinition. Il devient une structure de classe similaire au mécanisme de réflexion. Cette BeanDefinition obtenue en analysant les javabeans et les fichiers de configuration obtient la structure de base de l'assemblage d'un Javabean qui répond aux exigences. Si vous avez besoin de modifier le BeanDefinition en plus de la définition BeanDefinition, ce post-traitement est effectué. Le post-traitement est généralement traité via leprocesseur BeanfactoryPost dans le cadre de printemps.
Nous utilisons toujours les exemples que nous avons utilisés la dernière fois pour illustrer le principe de fonctionnement de ce haricot: il y a trois haricots, le module principal du module principal et les modules de dépendance dépendmodulea et dépendModuleB. Le premier dépend des deux derniers modules. Dans le fichier de configuration, nous déclarons généralement des dépendances comme celle-ci:
<? xml version = "1.0" encoding = "utf-8"?> <beans xmlns = "http://www.springframework.org/schema/beans" xmlns: xsi = "http://www.w3.org/2001/xmlschema-instance" XSI: ScheMalation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-Beans-3.0.xsd"> <bean id = "MainModule"> <Property Name = "Modulea"> <réf. <propriété name = "moduleB"> <ref bean = "moduleB" /> </ propriété> </ bean> <bean id = "modulea"> </ank> <bean id = "moduleB"> </ank> </bans>
Ceci est notre programme démontrant l'assemblage d'un conteneur standard de beanfactory (l'une des implémentations de conteneurs Spring IOC) dans le fichier de configuration ci-dessus:
classe MAINMODULE {private dépendModulea modulea; MODULEB DE DÉSENDUDEB PRIVÉB; public dépendModulea getmodulea () {return modulea; } public void setModulea (dépendModulea modulea) {this.modulea = modulea; } public dépendModuleB getModuleB () {return moduleB; } public void setModuleB (DependModuleB moduleB) {this.moduleB = moduleB; }} Interface dépendModulea {public void funcfrommodulea ();} interface dépendModuleB {public void funcfromuleb ();} classe dépendModuleAIMPl implémente dépendModulea {@Override public void funcfrommodulea () {System.out.println ("Il s'agit du Func de module de module a"); }} classe DependModuleBimplt implémente DependModuleB {@Override public void funcfromuleb () {System.out.println ("Ceci est func du module b"); }} classe publique SimpleIOCDemo {public static void main (String [] args) lève ClassNotFoundException {defaultListableBeAnfactory beanfactory = new defaultListableBeAnfactory (); XMLBeAnDefinitionReader Reader = new XMLBeAndeFinitionReader (BeanFactory); Reader.LoadBeAnDefinitions ("beans.xml"); MAINMODULE MAINMODULE = (MainModule) Beanfactory.getBean ("MainModule"); mainmodule.getModulea (). Funcfrommodulea (); mainmodule.getModuleB (). FuncFromuleB (); }}Ici, notre fichier de configuration et Javabean sont chargés et lus et analysés. Ici, le processus de génération et d'utilisation BeanDefinition y est caché. C'est le processus général qui se produit réellement à l'intérieur du CIO:
classe publique SimpleIOCDemo {public static void main (String [] args) lève classNotFoundException {defaultListableBeAnfactory beanfactory = new defaultListableBeAnfactory (); AbstractBeAnDefinition MainModule = new RootBeAnDefinition (MainModule.class); AbstractBeAnDefinition modulea = new rootBeAnDefinition (dépendModuleAIMPl.class); AbstractBeAnDefinition ModuleB = new rootBeAnDefinition (DependModuleBimpl.class); Beanfactory.RegisterBeAndefinition ("MainModule", MainModule); Beanfactory.RegisterBeAnDefinition ("modulea", modulea); beanfactory.RegisterBeAndefinition ("moduleB", moduleB); MutableProperTyValues PropertyValues = New MutableProperTyValues (); PropertyValues.add ("modulea", modulea); propriétéValues.add ("moduleB", moduleB); MainModule.SetPropertyValues (PropertyValues); Module MAINMODULE = (MainModule) beanfactory.getBean ("MainModule"); module.getModulea (). Funcfrommodulea (); module.getModuleB (). FuncFromuleB (); }}Après avoir chargé et lu les méta-informations du XML, le moteur d'analyse IOC créera le module mentionné dans une BeanDefinition basée sur son type réel. Ce haricot peut être considéré comme un processus de réflexion ou de proxy. Le but est de faire en sorte que le conteneur IOC efface la structure de haricots de l'objet d'instance à créer à l'avenir, puis d'enregistrer ces structures de haricots dans le haricotfactoire, puis d'ajouter les dépendances du module principal aux propriétés du module principal sous la forme de l'injection de set forme. Après cela, appelez simplement la méthode Getbean pour produire les haricots qui répondent aux exigences. C'est la prochaine étape du processus, et nous en parlerons plus tard.
Après avoir enregistré les informations sur le "dessin" de BeanDefinition au BeanFactory, nous pouvons toujours apporter des modifications à la définition BeanDefinition enregistrée. Il s'agit de l'un des aspects flexibles de la conception de printemps pour les utilisateurs mentionnés précédemment. Cela ne signifie pas que tous les processus sont incontrôlables, mais laissent beaucoup de place aux utilisateurs à jouer dans de nombreux endroits. La méthode spécifique consiste à utiliser le processeur Beanfactory BeanfactoryPostProcessor pour intervenir dans le traitement de Beanfactory pour réécrire davantage la partie BeanDefinition que nous devons modifier. Ce processus correspond au processus "post-traitement" dans le processus.
Prenant l'un des processeurs courants: le processeur de configuration d'attribut d'attribut à titre d'exemple, il s'agit de traiter le beanfactory enregistré après sa construction, de sorte que le contenu de l'attribut BeanDefinition correspondant soit modifié aux informations du processeur de configuration Fichier de configuration spécifié:
DefaultListableBeAnfactory Beanfactory = new defaultListableBeAnfactory (); XMLBeAndeFinitionReader Reader = new XMLBeAndeFinitionReader (BeanFactory); Reader.LoadBeanDeFinitions (new ClassPathResource ("Beans.xml")); PropertyPlaceHolderConfigurer Configurer Configurer = new PropertyPlaceHolderConfigurer (); configurer.SetLocation (new ClassPathResource ("About.Properties")); configurer.PostProcessBeanFactory (BeanFactory);Le BeanfactoryPostProcessor traitera le Beanfactory. Le résultat est de modifier certains attributs définis dans le BeanDefinition à certaines informations à l'emplacement du BeanfactoryPostProcessor.
2. Étape d'instanciation des haricots
Sous la direction des "dessins internes" traités de BeanDefinition, le conteneur peut encore transformer BeanDefiFnition en un objet d'instance activé existant dans la mémoire par la réflexion ou la production de bytecode dynamique CGLIB, puis assembler l'objet de dépendance spécifié par BeanDefinition dans la nouvelle instance d'objet d'instance par injection de setter ou injection d'initialisation. Ici, la référence de l'objet de dépendance est en fait attribuée aux attributs d'objet qui doivent être dépendants.
Mais il convient de noter ici que l'instance créée n'est pas seulement une instance simple de définition de bean, mais une instance Beanwrapper enveloppée par le printemps. Pourquoi devrait être utilisé pour envelopper le haricot dans la méthode Beanwrapper? Parce que BeanWrapper fournit une interface pour accéder aux propriétés de bean uniformément. Après avoir créé le framework de base de bean, les propriétés doivent être définies. La méthode du secteur de chaque bean est différente, elle sera donc très compliquée si vous la définissez directement avec réflexion. Par conséquent, Spring fournit ce wrapper pour simplifier les paramètres de la propriété:
Beanwrapper beanwrapper = new BeanwrapperImpl (class.forname ("com.rocking.demo.mainmodule")); beanwrapper.setpropertyvalue ("modulea", class.forname ("com.rocking.demo.sepropertyvalue"). Class.forname ("com.rocking.demo.depmoduleBimpl"). NewInstance ()); mainmodule mainmodule = (mainmodule) beanwrapper.getWrapyInstance (); mainmodule.getmodulea (). Funcfroma (); mainmodule.getmoduleb (). FuncFromb ();Le processus ci-dessus montre qu'au printemps, vous pouvez comprendre la structure du haricot d'instance qui est enveloppé à l'avenir en obtenant le conteneur réfléchissant de la classe et en faisant l'emballage. Utilisez la méthode de paramètre de propriété unifiée setpropertyValue pour définir des propriétés pour l'instance de ce package. L'instance de bean finale obtenue est obtenue via GetWrapyInstance, et vous pouvez constater que ses attributs ont été attribués avec succès.
Pour le moment, l'instance Bean est en fait complètement utilisable, mais Spring a également préparé des stratégies flexibles pour nous dans la phase d'instanciation pour terminer l'intervention de l'utilisateur à ce stade. Semblable à la concession dupostorprocesseur BeanfactoryPostor à la phase de démarrage du conteneur, pendant la phase d'instanciation, le printemps fournit un processeur BeanPostprocessor pour fonctionner sur les instances assemblées pour effectuer des modifications possibles:
Voici un exemple pour illustrer que vous définissez une classe d'implémentation BeanPostProcessor, implémentant les méthodes postprocessafterinitialisation et postprocess en avant de définir les opérations effectuées séparément après et avant l'assemblage d'instance Bean. Une fois que le beanfactory a ajouté ce processeur, chaque fois que la méthode GetBean assemblée, les deux méthodes seront appelées dans l'instance Bean assemblée en fonction du "dessin" (y compris l'instance dépendante créée pendant le processus d'assemblage). Ces méthodes peuvent modifier ces instances de bean.
Voici un exemple comme celui-ci (MainModule et ses dépendances sont les mêmes que ceux précédemment dans cet article):
classe modulec {chaîne privée x; String public getX () {return x; } public void setx (String x) {this.x = x; }} classe modulePostProcessor implémente beanPostProcessor {@Override Object Public PostProcessaFterinitialization (objet objet, String String) lève BeanSexception {System.out.println (String); if (objet instanceof modulec) {System.out.println (string); ((Modulec) objet) .setx ("après"); } return objet; } @Override Public Object PostProcessBeforeInitialization (Object Object, String String) lève BeanSexception {if (objet instanceof modulec) {((modulec) objet) .setx ("avant"); } return objet; }} classe publique très simpleiockernal {public static void main (String [] args) lève classnotfoundException, BeanSexception, instantiationException, illégalaccessException {defaultListableBeanFactory Beanfactory = new defAftListableBeanFactory (); XMLBeAnDefinitionReader Reader = new XMLBeAndeFinitionReader (BeanFactory); Reader.LoadBeAnDefinitions (new ClassPathResource ("beans.xml")); ModulePostProcessor postprocessor = new modulePostProcessor (); beanfactory.addBeanPostProcessor (postprocesseur); Module MAINMODULE = (MainModule) beanfactory.getBean ("MainModule"); Modulec modulec = (modulec) beanfactory.getBean ("modulec"); System.out.println (modulec.GetX ()); }}Il s'agit du fichier de configuration de dépendance pour le bean:
<? xml version = "1.0" encoding = "utf-8"?> <beans xmlns = "http://www.springframework.org/schema/beans" xmlns: xsi = "http://www.w3.org/2001/xmlschema-instance" XSI: ScheMalocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <Ebrot <propriété name = "modulea"> <ref bean = "modulea" /> </ propriété> <propriété name = "moduleB"> <ref bean = "moduleB" /> </ propriété> </ bean> <bean id = "modulea"> <propriété name = "infoa"> <value> $ {modulea.infoa} </value> </prewet> </pan> name = "Infob"> <value> Info de moduleB </value> </ propriété> </EAN> <bean id = "modulec"> </ank> </bans>D'après le résultat final, nous pouvons voir que chaque fois que l'instance GetBean (y compris générée par les dépendances) obtenue en appelant la méthode GetBean sera récupérée par le BeanPostProcessor pour le pré-traitement et le post-traitement.
En plus du traitement des haricots assemblés similaires à celle de BeanPostProcessor ci-dessus, Spring peut également définir des fonctions de rappel pour le processus d'initialisation et de destruction des haricots en configurant la méthode initiale et la méthode de détruire. Ces fonctions de rappel peuvent également fournir de manière flexible la possibilité de modifier les instances de bean.
L'ensemble du processus Spring IOC est en fait essentiellement le même que le prototype IOC que nous nous sommes écrit, sauf que la conception complexe permet au processus IOC de fournir aux utilisateurs un espace plus flexible et efficace. De plus, le CIO de Spring a également réalisé une conception exquise en termes de sécurité, de stabilité des conteneurs et de métadonnées en efficacité de conversion des haricots, ce qui rend la base du CIO, un conteneur de printemps, stable.