Frühlings -IOC -Prototyp
Der grundlegende Kern- und Ausgangspunkt des Spring -Frameworks ist zweifellos IOC. Als Kerntechnologie von Spring Containern hat IOC die Inversion von Abhängigkeiten erfolgreich abgeschlossen: vom aktiven Management der Hauptklasse von Abhängigkeiten bis zur globalen Kontrolle der Abhängigkeiten durch Springbehälter.
Was sind die Vorteile davon?
Natürlich ist es die sogenannte "Entkopplung", die die Beziehung zwischen den Modulen des Programms unabhängiger machen kann. Feder muss nur die Abhängigkeiten zwischen diesen Modulen steuern und diese Module basierend auf diesen Abhängigkeiten während des Start- und Initialisierungsprozesses des Containers erstellen, verwalten und verwalten. Wenn Sie die Abhängigkeiten zwischen Modulen ändern müssen, müssen Sie den Programmcode nicht einmal ändern. Sie müssen nur die geänderten Abhängigkeiten ändern. Spring wird diese neuen Abhängigkeiten beim Starten und Initialisieren des Containers wiederherstellen. In diesem Prozess muss festgestellt werden, dass der Code selbst die Erklärung der spezifischen Abhängigkeitssituation des Moduls nicht widerspiegeln muss, sondern nur die Schnittstelle des erforderlichen Moduls definieren muss. Daher ist dies eine typische interface-orientierte Idee. Gleichzeitig ist es am besten, die Abhängigkeiten in Form von Konfigurationsdateien oder Anmerkungen auszudrücken. Die relevanten Federverarbeitungsklassen montieren Module basierend auf diesen externen Konfigurationsdateien oder scannen die Annotation, um den internen Annotationsprozessor aufzurufen, um Module zusammenzustellen, um den IOC -Prozess abzuschließen.
Der Zweck von IOC ist eine Abhängigkeitsinjektion namens DI. Durch die IOC -Technologie hilft uns der ultimative Container, die Abhängigkeitsinjektion zwischen Modulen zu vervollständigen.
Darüber hinaus ist der letzte Punkt, dass wir im Prozess des Frühlings -IOC immer klar über die obige Hauptlinie sein müssen. Unabhängig davon, wie komplex die Echtzeit-Syntax und Klassenstruktur sind, sind die Funktion und sein Zweck gleich: Es wird die "Montage" des Moduls vervollständigen, indem sie sich auf die beschriebene Konfigurationsdatei als "Zeichnung" der Montage verlassen. Komplexe Syntax ist nur ein Mittel, um diesen Zweck zu erreichen.
Der sogenannte IOC-Prototyp, um das einfachste IOC-schematische Diagramm anzuzeigen, können wir genauso gut einen völlig einfachen Prototyp erstellen, um diesen Prozess zu veranschaulichen:
Erstens gibt es mehrere Module, die wir definieren, einschließlich des Hauptmoduls und des Abhängigkeitsmoduls, das durch die beiden Schnittstellen definiert ist:
Klasse MainModule {private DependModulea modulea; privat abhängigmoduleb moduleb; public abhängigModulea getModulea () {return modulea; } public void setModulea (abhängigModulea modulea) {this.modulea = modulea; } public DepelModuleb getModuleb () {return moduleb; } public void setModuleb (DependModuleb moduleb) {this.moduleb = moduleb; }} interface abhängigmodulea {public void funcFromMeSea ();} Schnittstelle abhängigModuleb {public void funcFromMulsB (); }} class DependModulebimpl implements DependModuleb {@Override public void funcFromMeMuleb () {System.out.println ("Dies ist Func aus dem Modul B"); }}Wenn wir IOC nicht übernehmen, sondern sich auf das Hauptmodul selbst verlassen, um die Erstellung seines abhängigen Moduls zu kontrollieren, wird es so sein:
public class Simpleiocdemo {public static void main (String [] args) löst classNotFoundException {MainModule MainModule = new MainModule () aus; MainModule.SetModulea (neu abhängigModuleAmpl ()); MainModule.SetModuleb (neu DependModulebImpl ()); MainModule.getModulea (). MainModule.getModuleb (). }}Dies ist unsere vereinfachte Definition des IOC -Containerprototyps. Wenn der Container nach dem Start initialisiert wird, wird die vom Benutzer geschriebene Konfigurationsdatei gelesen. Hier nehmen wir die einfache Eigenschaftenkonfigurationsdatei als Beispiel auf. Erst wenn der Benutzer die GetBean -Methode aufruft, wird die entsprechende Bean wirklich zusammengestellt und gemäß der Konfigurationsdatei geladen. Eine Karte, die zum Speichern der zusammengesetzten Bohne verwendet wird, wird innerhalb des von uns definierten Containerprototyps gehalten. Wenn es eine Bohne gibt, die den Anforderungen entspricht, muss sie nicht erneut erstellt werden:
Klasse SimpleIoccontainer {private properties properties = new Properties (); private map <String, Objekt> modulemap = new HashMap <> (); {try {properties.load (new FileInputStream (neue Datei ("simpleioc.properties")); } catch (Ausnahme e) {e.printstacktrace (); }} public Object getBean (String -ModulenName) löst ClassNotFoundException {Object InstanceObj; if (modulemap.get (modulename)! = null) {System.out.println ("alte Bean zurückgeben"); return modulemap.get (modulenName); } System.out.println ("Neue Bean erstellen"); String FullClassName = Properties.getProperty (ModulenName); if (fullClassName == null) werfen neue classNotFoundException (); sonst {Klasse <? erweitert Objekt> clazz = class.forname (fullClassName); try {instanceObj = clazz.newinstance (); InstanceObj = BuildAttachedModules (ModulenName, InstanceObj); modulemap.put (ModulenName, InstanceObj); RückkehrinstanceObj; } catch (InstantiationException e) {e.printstacktrace (); } catch (illegalAccessException e) {e.printstacktrace (); }} return null; } private Object BuildAttachedModules (String -Modulenname, ObjektinstanceObj) {set <string> propertieskeys = properties.stringPropertynames (); Field [] fields = instanceobj.getClass (). GetDeclaredfields (); für (String -Schlüssel: Propertieskeys) {if (key.contains (modulename) &&! key.equals (modulenName)) {try {class <? erweitert Object> clazz = class.forname (Properties.getProperty (Properties.getProperty (Schlüssel))); für (Feldfeld: Felder) {if (field.gettype (). isassignableFrom (clazz)) field.set (InstanceObj, clazz.newinstance ()); }} catch (Ausnahme e) {e.printstacktrace (); }}} return InstanceObj; }}Dies ist die Abhängigkeitskonfigurationsdatei, die wir mit der Eigenschaftenkonfigurationsdatei geschrieben haben. Diese Konfigurationsdatei ist die "Zeichnung" unseres Montagemoduls. Die Syntax hier ist vollständig von uns definiert. Im realen Feder -IOC -Container wird eine komplexere Abhängigkeitslogik eine stärker entwickelte Konfigurationsdatei für XML -Format oder eine neuere Annotationskonfiguration verwendet, und der Annotationsprozessor wird verwendet, um die Analyse der Zeichnung zu vervollständigen:
MainModule = com.rock.demo.Mainmodulemainmodule.modulea = moduleamAnmodule.moduleb = modulebmodulea = com.rocking.dependmoduleAmplmoduleb = com.rock.demo.dependmodulebimpl
Dies ist der Testcode. Es ist zu sehen, dass wir Module, die die Anforderungen über den von uns definierten IOC -Container entsprechen, vollständig erhalten können. Gleichzeitig können wir auch feststellen, dass der von uns definierte Behälter diese Bohnen für uns aufrechterhalten kann. Wenn eine Bohne zusammengebaut und erstellt wurde, muss sie nicht wieder erstellt werden.
public class SimpleIocdemo {public static void main (String [] args) löst classNotFoundException {simpleioccontainer container = new SimpleIoccontainer () aus; AbhängigModulea modulea = (abhängigModulea) Container.getBean ("modulea"); modulea.funcFromMeea (); AbhängigModuleb moduleb = (abhängigModuleb) container.getbean ("moduleb"); moduleb.funcFromMuleb (); MainModule MainModule = (MainModule) Container.getbean ("MainModule"); MainModule.getModulea (). MainModule.getModuleb (). Container.getbean ("Mainmodule"); }}Dies ist der IOC -Containerprototyp, den ich basierend auf der Grundidee von IOC erstellt habe. Obwohl Spring IOC eine komplexe Syntax hat, sind die am Ende erledigten Aufgaben im Kern gleich. Sogenannte "Alle Änderungen werden nicht von ihrer Essenz getrennt".
Feder -IOC -spezifischer Prozess
Letztes Mal wurde der Prototyp der allgemeinen Implementierung von IOC gezeigt. So implementieren Sie also speziell den Prozess des Ladens von Pojos im Spring -Framework dieses Containers basierend auf der Metadaten -Meta -Informationskonfiguration? Es gibt viele Orte im gesamten Arbeitsprozess für IOC -Container in Spring IOC, die ziemlich flexibel sind und den Benutzern viel Platz bieten, um ihre eigenen Aufgaben zu erledigen, anstatt nur den mechanischen Prozess des Containers abzuschließen.
Dies ist das Prozessdiagramm des gesamten IOC -Containerarbeitsprozesses:
1. Container -Startstufe (1) Laden von Konfigurationsdateiinformationen (2) Analyse der Konfigurationsdateiinformationen (3) Assembly BeanDefinition
(4) Erstens nach der Verarbeitung werden Meta-Information wie Konfigurationsdateien oder Anmerkungen und JavaBean-Klasseninformationen in den IOC-Container geladen. Der Container liest eine XML-Format-Konfigurationsdatei. Diese Konfigurationsdatei ist eine vom Benutzer und der Montage deklarierte Abhängigkeit, die besondere Aufmerksamkeit benötigt. Es ist eine frühe "externe Zeichnung" zum Zusammenstellen der Bohne. Der Parsing-Engine im Behälter kann die Charakter-Meta-Information in der Textform analysieren, die wir in eine BeanDefinition schreiben, die im Container erkannt werden kann, was die BeanDefinition verstehen kann. Es wird zu einer Klassenstruktur, die dem Reflexionsmechanismus ähnelt. Diese Beandefinition wird erhalten, die durch Analyse von JavaBeans und Konfigurationsdateien erhalten wird, die grundlegende Struktur des Zusammenstellens eines Javabeins, das den Anforderungen entspricht. Wenn Sie die BeanDefinition zusätzlich zur BeanDefinition ändern müssen, wird diese Nachbearbeitung durchgeführt. Die Nachbearbeitung wird im Frühjahrsgerüst im Allgemeinen über den BeanfactoryPostProcessor verarbeitet.
Wir verwenden immer noch die Beispiele, die wir beim letzten Mal verwendet haben, um das Betriebsprinzip dieser BeanDefinition zu veranschaulichen: Es gibt drei Bohnen, das Hauptmodul -Mainmodul und die Abhängigkeitsmodule Depelmodulea und Depelmoduleb. Ersteres hängt von den beiden letzteren Modulen ab. In der Konfigurationsdatei deklarieren wir im Allgemeinen solche Abhängigkeiten wie folgt:
<? XSI: Schemalocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-Beans-3.0.xsd"> <bean id = "modula/modula"/achbetriebsname "/achb." <Eigenschaft name = "moduleb"> <ref bean = "moduleb"/> </property> </bean> <bean id = "modulea"> </bean> <bean id = "moduleb"> </bean> </beans>
Dies ist unser Programm, das die Montage eines Standard -Beanfaktor -Containers (eine der Implementierungen von Feder -IOC -Containern) in die obige Konfigurationsdatei demonstriert:
Klasse MainModule {private DependModulea modulea; privat abhängigmoduleb moduleb; public abhängigModulea getModulea () {return modulea; } public void setModulea (abhängigModulea modulea) {this.modulea = modulea; } public DepelModuleb getModuleb () {return moduleb; } public void setModuleb (DependModuleb moduleb) {this.moduleb = moduleb; }} interface abhängigmodulea {public void funcFromMeSea ();} Schnittstelle abhängigModuleb {public void funcFromMulsB (); }} class DependModulebimpl implements DependModuleb {@Override public void funcFromMeMuleb () {System.out.println ("Dies ist Func aus dem Modul B"); }} public class SimpleIocDemo {public static void main (String [] args) löscht classNotFoundException {defaultListableBeArtory beanfactory = new DefaultListableBeAnfactory (); XmlbeanDeFinitionReader reader = new XmlbeanDeFinitionReader (Beanfactory); Reader.loadbeanDeFinitions ("Beans.xml"); MainModule MainModule = (MainModule) beanfactory.getBean ("Mainmodule"); MainModule.getModulea (). MainModule.getModuleb (). }}Hier werden unsere Konfigurationsdatei und JavaBean geladen und gelesen und analysiert. Hier ist der Beandefinition -Erzeugungs- und -nutzungsprozess darin versteckt. Dies ist der allgemeine Prozess, der tatsächlich im IOC passiert:
public class SimpleIocDemo {public static void main (String [] args) löst classNotFoundException {StandardListableBeanFactory beanfactory = new StandardListableBeanFactory (); AbstractbeanDeFinition mainmodule = new RootbeanDeFinition (MainModule.class); AbstractBeandefinition modulea = new RootbeanDeFinition (abhängigModuleAmpl.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); PropertyValues.add ("Moduleb", Moduleb); MainModule.SetPropertyValues (PropertyValues); MainModule modul = (MainModule) beanfactory.getBean ("Mainmodule"); modul.getModulea (). modul.getModuleb (). }}Nach dem Laden und Lesen der Meta -Informationen des XML erstellt die IOC -Parsing -Engine das darin erwähnte Modul, das auf Basis seines tatsächlichen Typs in eine BeanDefinition erwähnt wird. Diese BeanDefinition kann als Reflexions- oder Proxy -Prozess angesehen werden. Ziel ist es, den IOC -Container die Bohnenstruktur des in Zukunft erstellten Instanzobjekts zu entfernen, und dann diese Bohnenstrukturen in die Beanfakeration zu registrieren und dann die Abhängigkeiten des Hauptmoduls der Eigenschaften des Hauptmoduls in Form von Setzerinjektion hinzuzufügen (dies hängt von der Setter -Methode ab. Beanfactory hat bereits Gestalt angenommen. Rufen Sie danach einfach die GetBean -Methode an, um die Bohnen zu produzieren, die den Anforderungen entsprechen. Dies ist die nächste Phase des Prozesses, und wir werden später darüber sprechen.
Nachdem wir die Informationen zur "Zeichnung" von BeanDefinition an die Beanfactory registriert haben, können wir immer noch Änderungen an der registrierten BeanDefinition vornehmen. Dies ist einer der flexiblen Aspekte des Frühlingsdesigns für Benutzer, die zuvor erwähnt wurden. Es bedeutet nicht, dass alle Prozesse unkontrollierbar sind, sondern viel Platz für Benutzer an vielen Orten spielen können. Die spezifische Methode besteht darin, den Beanfactory -Prozessor beanfactoryPostProcessor zu verwenden, um in die Verarbeitung von Beanfactory einzugreifen, um den von uns geänderten BeanDefinition -Teil weiter umzuschreiben. Dieser Prozess entspricht dem Prozess "Nachbearbeitung" im Prozess.
Einen der gängigen Prozessoren: Attribut -Platzhalter -Konfigurationsprozessor als Beispiel ist die Verarbeitung des registrierten Beanfaktoriums nach dem Erstellen, damit der Inhalt in der entsprechenden Beandefinition -Attribut an den Informationen im Konfigurationsprozessor angegeben wird: Konfigurationsdatei:
StandardListableBeanFactory beanfactory = new DefaultListableBeanFactory (); xmlbeandeFinitionReader Reader = new XmlbeandeFinitionReader (BeanFactory); Leser.loadbeandeFinitions (New ClasspatheResource ("Beans.xml");););); PropertyPlaPleholderConFigurer (); configurer.setLocation (neuer classpatResource ("About.Properties"); configurer.postProcessbeanFactory (beanfactory);Der BeanfactoryPostProcessor wird die Beanfaktorie verarbeiten. Das Ergebnis besteht darin, einige in der BeanDefinition definierte Attribute auf einige Informationen am Standort des BeanfactoryPostprozessors zu ändern.
2. Bean -Instanziationsphase
Unter der Leitung der verarbeiteten "internen Zeichnungen" der BeanDefinition kann der Container Beandefifnition in ein im Speicher vorhandenes aktiviertes Instanzobjekt durch Reflexion oder CGGLIB Dynamic Bytecode Production weiter verwandeln und dann das Abhängigkeitsobjekt zusammenstellen, das durch BeanDefinition in das neu erstellte Instanzobjekt durch Setter -Injektion oder Initialisierungseinstellung oder Initialisierungseinstellung angegeben ist. Hier wird die Referenz des Abhängigkeitsobjekts tatsächlich den Objektattributen zugeordnet, von denen abhängig sein muss.
Es sollte jedoch hier angemerkt werden, dass die erstellte Instanz nicht nur eine einfache Instanz der Bean -Definition ist, sondern auch eine von Frühling eingewickelte Beanwrapper -Instanz. Warum sollte die Bohne in die Beanwrapper -Methode einwickelt werden? Da BeanWrapper eine Schnittstelle zur Einheitlichkeit von Bean -Eigenschaften bietet. Nach dem Erstellen des grundlegenden Bean -Frameworks müssen die darin enthaltenen Eigenschaften festgelegt werden. Die Settermethode jeder Bohne ist unterschiedlich, daher ist sie sehr kompliziert, wenn Sie sie direkt mit Reflexion einstellen. Daher stellt die Spring diesen Wrapper zur Vereinfachung der Eigenschaftseinstellungen zur Verfügung:
Beanwrapper Beanwrapper = new Beanwrapperimpl (class.forname ("com.rocking.demo.mainmodule"); Beanwrapper.setPropertyValue ("modulea", class.ForName ("com.rocking.demo.depmoduleaimplover"). NewInstance (). NewInstance (). NewInstance (). "moduleb", class.forname ("com.rocking.demo.depmodulebimpl"). newInstance ()); Mainmodule MainModule = (MainModule) BeanWrapper.GetwrappedInstance (); Mainmodule.getModulea ().Der obige Vorgang zeigt, dass Sie im Frühjahr die Struktur der Instanzbohne verstehen können, die in Zukunft eingewickelt wird, indem Sie den reflektierenden Behälter der Klasse erhalten und die Verpackung herstellen. Verwenden Sie die Einstellungsmethode der Unified Property -Einstellungsmethode SetPropertyValue, um Eigenschaften für die Instanz dieses Pakets festzulegen. Die endgültige Bean -Instanz wird durch GetwrappedInstance erhalten, und Sie können feststellen, dass seine Attribute erfolgreich zugewiesen wurden.
Zu diesem Zeitpunkt ist die Bean -Instanz tatsächlich vollständig verwendbar, aber die Spring hat auch flexible Strategien für uns in der Instanziationsphase vorbereitet, um die Intervention des Benutzers in dieser Phase zu vervollständigen. Ähnlich wie bei der Beandefinition der BeanfactoryPostProcessor -Steuerung in der Startphase der Container bietet der Spring in der Instanziierung einen BeanPostPostProcessor -Prozessor für die montierten Instanzen, um mögliche Änderungen vollständig zu vervollständigen:
Hier ist ein Beispiel, um zu veranschaulichen, dass Sie eine BeanPostPostProcessor -Implementierungsklasse definieren, in der die Methoden postprozessafterinitialisierung und postprozessbeschoneritialisierung implementiert werden, um die Operationen, die nach und vor der Bean -Instanzmontage getrennt durchgeführt wurden, zu definieren. Nachdem der Beanfactory diesen Prozessor hinzugefügt hat, werden die beiden Methoden jedes Mal, wenn die GetBean -Methode zusammengebaut, in der Bean -Instanz aufgerufen, die gemäß der "Zeichnung" zusammengestellt wurde (einschließlich der abhängigen Instanzbohne, die während des Montageprozesses erstellt wurde). Diese Methoden können diese Bean -Instanzen ändern.
Hier ist ein Beispiel wie dieses (Mainmodule und seine Abhängigkeiten sind die gleichen wie in diesem Artikel):
Klassenmodulec {private String x; public String getX () {return x; } public void setx (String x) {this.x = x; }} class modulepostProcessor implementiert BeanPostProcessor {@Override public Object postProcessAfterInitialization (Objekt Objekt, String String) löscht beansexception {system.out.println (String) aus; if (Objektinstanzmodulec) {System.out.println (String); ((Modulec) -Objekt) .setX ("nach"); } Rückgabeobjekt; } @Override public Object postProcessBeforeinitialisierung (Objektobjekt, String String) löscht Beansexception {if (ObjectinstanceOf modulec) {((modulec) -Objekt) .setX ("vor"); } Rückgabeobjekt; }} public class SEHRSimpleIockernal {public static void main (string [] args) löst classNotFoundException, Beansexception, InstanziationException, illegalAccessexception {StandardsListableBeanFactory beanfactory = new DefaultLableBeAnfactory () aus. XmlbeanDeFinitionReader reader = new XmlbeanDeFinitionReader (Beanfactory); Reader.loadbeanDeFinitions (neuer classpathresource ("beans.xml"); ModulepostProcessor postprozessor = new ModulepostProcessor (); BeanFactory.AddbeanPostProcessor (Postprozessor); MainModule modul = (MainModule) beanfactory.getBean ("Mainmodule"); Modulec modulec = (modulec) beanfactory.getbean ("modulec"); System.out.println (modulec.getX ()); }}Dies ist die Abhängigkeitskonfigurationsdatei für die Bean:
<? XSI: Schemalocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" name="moduleA"> <ref bean="moduleA"/> </property> <property name="moduleB"> <ref bean="moduleB"/> </property> </bean> <bean id="moduleA"> <property name="infoA"> <value>${moduleA.infoA}</value> </property> </bean> <bean id="moduleB"> <property name = "infob"> <wert> Info von Moduleb </value> </property> </bean> <bean id = "modulec"> </bean> </beans>Aus dem Endergebnis können wir erkennen, dass jedes Mal, wenn die GetBean-Instanz (einschließlich der von Abhängigkeiten generierten) erhalten wird, die durch Aufrufen der GetBean-Methode vom BeanPostProcessor zur Vor- und Nachbearbeitung abgerufen wird.
Zusätzlich zur Verarbeitungsversammlung, die dem oben genannten Bohnenpostprozessor ähnlich verarbeitet, kann die Spring auch Rückruffunktionen für den Initialisierungs- und Zerstörungsprozess von Beans durch Konfiguration von Init-Methoden und Destroy-Methoden festlegen. Diese Rückruffunktionen können auch flexibel die Möglichkeit bieten, Bean -Instanzen zu ändern.
Der gesamte Frühlings -IOC -Prozess ist im Wesentlichen der gleiche wie der IOC -Prototyp, den wir selbst geschrieben haben, außer dass das komplexe Design es dem IOC -Prozess ermöglicht, Benutzern flexibler und effektiver zu bieten. Darüber hinaus hat das IOC von Spring in Bezug auf Sicherheit, Containerstabilität und Metadaten zur Bean -Umwandlungseffizienz exquisites Design erreicht, wodurch der Grundriss für IOC, ein Stallf in den Frühlingsbehältern, geführt wird.