Implementieren Ihres IoC-Containers

Bild

Einführung


Jeder unerfahrene Entwickler sollte mit dem Konzept der Inversion of Control vertraut sein.

Fast jedes neue Projekt beginnt nun mit der Wahl eines Frameworks, mit dem das Prinzip der Abhängigkeitsinjektion umgesetzt wird.

Inversion of Control (IoC) ist ein wichtiges Prinzip der objektorientierten Programmierung, mit dem die Kohärenz in Computerprogrammen verringert wird, und eines der fünf wichtigsten Prinzipien von SOLID.

Heute gibt es zu diesem Thema mehrere wichtige Rahmenbedingungen:

1. Dolch
2. Google Guice
3. Frühlingsrahmen

Ich benutze Spring immer noch und bin teilweise zufrieden mit seiner Funktionalität, aber es ist Zeit, etwas und mein eigenes auszuprobieren, nicht wahr?

Über mich


Mein Name ist Nikita, ich bin 24 Jahre alt und mache seit 3 ​​Jahren Java (Backend). Er lernte nur mit praktischen Beispielen und versuchte gleichzeitig, die Flecken des Unterrichts zu verstehen. Im Moment arbeite ich (freiberuflich) und schreibe CMS für ein kommerzielles Projekt, bei dem ich Spring Boot verwende. Kürzlich besuchte ich den Gedanken: „Warum schreiben Sie Ihren IoC (DI) -Container nicht entsprechend Ihrer Vision und Ihrem Wunsch?“. Grob gesagt - "Ich wollte mein eigenes mit Blackjack ...". Dies wird heute diskutiert. Nun, bitte unter Katze. Link zu Projektquellen .

Eigenschaften


- Das Hauptmerkmal des Projekts ist die Abhängigkeitsinjektion.
Es werden 3 Hauptmethoden zur Abhängigkeitsinjektion unterstützt:
  1. Klassenfelder
  2. Klassenkonstruktor
  3. Klassenfunktionen (Standardsetzer für einen Parameter)

* Hinweis:
- Wenn beim Scannen einer Klasse alle drei Injektionsmethoden gleichzeitig verwendet werden, hat die Injektionsmethode über den Konstruktor der Klasse, die mit der Annotation @IoCDependency gekennzeichnet ist, Priorität. Das heißt, Es funktioniert immer nur eine Injektionsmethode.

- verzögerte Initialisierung von Komponenten (auf Anfrage);

- Integrierte Loader-Konfigurationsdateien (Formate: INI, XML, Eigenschaften);

- Befehlszeilenargument-Handler;

- Verarbeitung von Modulen durch Schaffung von Fabriken;

- integrierte Ereignisse und Zuhörer;

- integrierte Informanten (Sensibles) zum "Informieren" einer Komponente, Fabrik, eines Listeners, eines Prozessors (ComponentProcessor) darüber, dass bestimmte Informationen je nach Informant in das Objekt geladen werden sollen;

- Ein Modul zum Verwalten / Erstellen eines Thread-Pools, zum Deklarieren von Funktionen als ausführbare Aufgaben für einige Zeit und zum Initialisieren dieser Funktionen in der Pool-Factory sowie zum Starten mit SimpleTask-Parametern.

Wie erfolgt das Scannen von Paketen:
Es verwendet eine Reflections-API eines Drittanbieters mit einem Standardscanner.

//{@see IocStarter#initializeContext} private AppContext initializeContext(Class<?>[] mainClasses, String... args) throws Exception { final AppContext context = new AppContext(); for (Class<?> mainSource : mainClasses) { final List<String> modulePackages = getModulePaths(mainSource); final String[] packages = modulePackages.toArray(new String[0]); final Reflections reflections = ReflectionUtils.configureScanner(packages, mainSource); final ModuleInfo info = getModuleInfo(reflections); initializeModule(context, info, args); } Runtime.getRuntime().addShutdownHook(new ShutdownHook(context)); context.getDispatcherFactory().fireEvent(new OnContextIsInitializedEvent(context)); return context; } 

Wir erhalten eine Sammlung von Klassen mit Filtern von Anmerkungen und Typen.
In diesem Fall sind dies @IoCComponent, @Property und Progenitor Analyzer <R, T>

Kontextinitialisierungsreihenfolge:
1) Zunächst werden die Konfigurationstypen initialisiert.
 //{@see AppContext#initEnvironment(Set)} public void initEnvironment(Set<Class<?>> properties) { for (Class<?> type : properties) { final Property property = type.getAnnotation(Property.class); if (property.ignore()) { continue; } final Path path = Paths.get(property.path()); try { final Object o = type.newInstance(); PropertiesLoader.parse(o, path.toFile()); dependencyInitiator.instantiatePropertyMethods(o); dependencyInitiator.addInstalledConfiguration(o); } catch (Exception e) { throw new Error("Failed to Load " + path + " Config File", e); } } } 

* Erklärungen:
Anmerkung @Property hat einen erforderlichen Zeichenfolgenparameter - Pfad (Pfad zur Konfigurationsdatei). Hier wird die Datei durchsucht, um die Konfiguration zu analysieren.
Die PropertiesLoader- Klasse ist eine Dienstprogrammklasse zum Initialisieren der Felder der Klasse, die den Feldern der Konfigurationsdatei entsprechen.
Funktion DependencyFactory # addInstalledConfiguration (Object) - Lädt das Konfigurationsobjekt als SINGLETON in die Factory (andernfalls ist es sinnvoll, die Konfiguration nicht bei Bedarf neu zu laden).

2) Initialisierung der Analysatoren
3) Initialisierung der gefundenen Komponenten (Klassen, die mit der Annotation @IoCComponent gekennzeichnet sind)
 //{@see AppContext#scanClass(Class)} private void scanClass(Class<?> component) { final ClassAnalyzer classAnalyzer = getAnalyzer(ClassAnalyzer.class); if (!classAnalyzer.supportFor(component)) { throw new IoCInstantiateException("It is impossible to test, check the class for type match!"); } final ClassAnalyzeResult result = classAnalyzer.analyze(component); dependencyFactory.instantiate(component, result); } 

* Erklärungen:
Die ClassAnalyzer-Klasse - definiert die Abhängigkeitsinjektionsmethode, auch wenn Fehler bei der falschen Platzierung von Anmerkungen, Konstruktordeklarationen und Parametern in der Methode vorliegen - gibt einen Fehler zurück. Funktionsanalysator <R, T> #analyze (T) - gibt das Ergebnis der Analyse zurück. Function Analyzer <R, T> #supportFor (T) - Gibt abhängig von den angegebenen Bedingungen einen booleschen Parameter zurück.
Funktion DependencyFactory # instantiate (Class, R) - Installiert den Typ mithilfe der von ClassAnalyzer definierten Methode in der Factory oder löst eine Ausnahme aus, wenn Fehler in der Analyse oder beim Initialisieren des Objekts auftreten.

3) Scanmethoden
- Methode zum Einfügen von Parametern in den Klassenkonstruktor
  private <O> O instantiateConstructorType(Class<O> type) { final Constructor<O> oConstructor = findConstructor(type); if (oConstructor != null) { final Parameter[] constructorParameters = oConstructor.getParameters(); final List<Object> argumentList = Arrays.stream(constructorParameters) .map(param -> mapConstType(param, type)) .collect(Collectors.toList()); try { final O instance = oConstructor.newInstance(argumentList.toArray()); addInstantiable(type); final String typeName = getComponentName(type); if (isSingleton(type)) { singletons.put(typeName, instance); } else if (isPrototype(type)) { prototypes.put(typeName, instance); } return instance; } catch (Exception e) { throw new IoCInstantiateException("IoCError - Unavailable create instance of type [" + type + "].", e); } } return null; } 

- Methode zum Einfügen von Parametern in Klassenfelder
  private <O> O instantiateFieldsType(Class<O> type) { final List<Field> fieldList = findFieldsFromType(type); final List<Object> argumentList = fieldList.stream() .map(field -> mapFieldType(field, type)) .collect(Collectors.toList()); try { final O instance = ReflectionUtils.instantiate(type); addInstantiable(type); for (Field field : fieldList) { final Object toInstantiate = argumentList .stream() .filter(f -> f.getClass().getSimpleName().equals(field.getType().getSimpleName())) .findFirst() .get(); final boolean access = field.isAccessible(); field.setAccessible(true); field.set(instance, toInstantiate); field.setAccessible(access); } final String typeName = getComponentName(type); if (isSingleton(type)) { singletons.put(typeName, instance); } else if (isPrototype(type)) { prototypes.put(typeName, instance); } return instance; } catch (Exception e) { throw new IoCInstantiateException("IoCError - Unavailable create instance of type [" + type + "].", e); } } 

- Methode zum Einfügen von Parametern durch Klassenfunktionen
  private <O> O instantiateMethodsType(Class<O> type) { final List<Method> methodList = findMethodsFromType(type); final List<Object> argumentList = methodList.stream() .map(method -> mapMethodType(method, type)) .collect(Collectors.toList()); try { final O instance = ReflectionUtils.instantiate(type); addInstantiable(type); for (Method method : methodList) { final Object toInstantiate = argumentList .stream() .filter(m -> m.getClass().getSimpleName().equals(method.getParameterTypes()[0].getSimpleName())) .findFirst() .get(); method.invoke(instance, toInstantiate); } final String typeName = getComponentName(type); if (isSingleton(type)) { singletons.put(typeName, instance); } else if (isPrototype(type)) { prototypes.put(typeName, instance); } return instance; } catch (Exception e) { throw new IoCInstantiateException("IoCError - Unavailable create instance of type [" + type + "].", e); } } 



Benutzer-API
1. ComponentProcessor - Ein Dienstprogramm, mit dem Sie eine Komponente nach Belieben ändern können, sowohl vor als auch nach ihrer Initialisierung.
 public interface ComponentProcessor { Object afterComponentInitialization(String componentName, Object component); Object beforeComponentInitialization(String componentName, Object component); } 


* Erklärungen:
Funktion #afterComponentInitialization (String, Object) - ermöglicht die Bearbeitung der Komponente nach der Initialisierung im Kontext, eingehende Parameter - (fester Name der Komponente, instanziiertes Objekt der Komponente).
Mit der Funktion #beforeComponentInitialization (String, Object) können Sie die Komponente bearbeiten, bevor Sie sie im Kontext initialisieren. Eingehende Parameter - (fester Name der Komponente, instanziiertes Objekt der Komponente).

2. CommandLineArgumentResolver
 public interface CommandLineArgumentResolver { void resolve(String... args); } 


* Erklärungen:
Die Funktion #resolve (String ...) ist eine Schnittstelle, die verschiedene Befehle verarbeitet, die beim Start der Anwendung über cmd gesendet werden. Der Eingabeparameter ist ein unbegrenztes Array von Befehlszeilenzeichenfolgen (Parametern).
3. Informanten (Sensibles) - Gibt an, dass die untergeordnete Klasse des Informanten opr einbetten muss. Funktionalität abhängig von der Art des Informanten (ContextSensible, EnvironmentSensible, ThreadFactorySensible usw.)

4. Zuhörer
Die Listener-Funktionalität ist implementiert, die Multithreading-Ausführung wird mit der empfohlenen Anzahl von Deskriptoren garantiert, die für optimierte Ereignisse konfiguriert sind.
 @org.di.context.annotations.listeners.Listener //  - @IoCComponent //  ,       (Sensibles)   . public class TestListener implements Listener { private final Logger log = LoggerFactory.getLogger(TestListener.class); @Override public boolean dispatch(Event event) { if (OnContextStartedEvent.class.isAssignableFrom(event.getClass())) { log.info("ListenerInform - Context is started! [{}]", event.getSource()); } else if (OnContextIsInitializedEvent.class.isAssignableFrom(event.getClass())) { log.info("ListenerInform - Context is initialized! [{}]", event.getSource()); } else if (OnComponentInitEvent.class.isAssignableFrom(event.getClass())) { final OnComponentInitEvent ev = (OnComponentInitEvent) event; log.info("ListenerInform - Component [{}] in instance [{}] is initialized!", ev.getComponentName(), ev.getSource()); } return true; } } 

** Erklärungen:
Die Versandfunktion (Ereignis) ist die Hauptfunktion des Systemereignishandlers.
- Es gibt Standardimplementierungen von Listenern mit Überprüfung auf Ereignistypen sowie mit integrierten Benutzerfiltern {@link Filter}. Im Paket enthaltene Standardfilter: AndFilter, ExcludeFilter, NotFilter, OrFilter, InstanceFilter (benutzerdefiniert). Standard-Listener-Implementierungen: FilteredListener und TypedListener. Der erste verwendet einen Filter, um das eingehende Ereignisobjekt zu überprüfen. Der zweite prüft das Ereignisobjekt oder ein anderes Objekt auf Zugehörigkeit zu einer bestimmten Instanz.



Module
1) Modul zum Arbeiten mit Streaming-Aufgaben in Ihrer Anwendung

- Abhängigkeiten verbinden
 <repositories> <repository> <id>di_container-mvn-repo</id> <url>https://raw.github.com/GenCloud/di_container/threading/</url> <snapshots> <enabled>true</enabled> <updatePolicy>always</updatePolicy> </snapshots> </repository> </repositories> <dependencies> <dependency> <groupId>org.genfork</groupId> <artifactId>threads-factory</artifactId> <version>1.0.0.RELEASE</version> </dependency> </dependencies> 


- Anmerkungsmarker für die Aufnahme des Moduls in den Kontext (@ThreadingModule)
 @ThreadingModule @ScanPackage(packages = {"org.di.test"}) public class MainTest { public static void main(String... args){ IoCStarter.start(MainTest.class, args); } } 


- Implementierung der Modulfactory in die installierte Komponente der Anwendung
 @IoCComponent public class ComponentThreads implements ThreadFactorySensible<DefaultThreadingFactory> { private final Logger log = LoggerFactory.getLogger(AbstractTask.class); private DefaultThreadingFactory defaultThreadingFactory; private final AtomicInteger atomicInteger = new AtomicInteger(0); @PostConstruct public void init() { defaultThreadingFactory.async(new AbstractTask<Void>() { @Override public Void call() { log.info("Start test thread!"); return null; } }); } @Override public void threadFactoryInform(DefaultThreadingFactory defaultThreadingFactory) throws IoCException { this.defaultThreadingFactory = defaultThreadingFactory; } @SimpleTask(startingDelay = 1, fixedInterval = 5) public void schedule() { log.info("I'm Big Daddy, scheduling and incrementing param - [{}]", atomicInteger.incrementAndGet()); } } 

* Erklärungen:
ThreadFactorySensible ist eine der untergeordneten Informantenklassen für die Implementierung in die instanziierte Komponente von ODA. Informationen (Konfiguration, Kontext, Modul usw.).
DefaultThreadingFactory - Factory-Fabrik für Threading-Factory-Module.

Annotation @SimpleTask ist eine parametrierbare Markierungsanmerkung zum Identifizieren der Implementierung von Aufgaben in Funktionen durch die Komponente. (Startet den Stream mit den angegebenen Parametern mit einer Anmerkung und fügt ihn der Factory hinzu, von wo aus er abgerufen werden kann, und deaktiviert beispielsweise die Ausführung.)

- Standardfunktionen für die Aufgabenverteilung
  //   . ,  ,       . <T> AsyncFuture<T> async(Task<T>) //      . <T> AsyncFuture<T> async(long, TimeUnit, Task<T>) //      . ScheduledAsyncFuture async(long, TimeUnit, long, Runnable) 


*** Bitte beachten Sie, dass die Ressourcen im geplanten Thread-Pool begrenzt sind und Aufgaben schnell erledigt werden müssen.

- Standardpoolkonfiguration
 # Threading threads.poolName=shared threads.availableProcessors=4 threads.threadTimeout=0 threads.threadAllowCoreTimeOut=true threads.threadPoolPriority=NORMAL 




Ausgangspunkt oder wie alles funktioniert


Wir verbinden Projektabhängigkeiten:

  <repositories> <repository> <id>di_container-mvn-repo</id> <url>https://raw.github.com/GenCloud/di_container/context/</url> <snapshots> <enabled>true</enabled> <updatePolicy>always</updatePolicy> </snapshots> </repository> </repositories> ... <dependencies> <dependency> <groupId>org.genfork</groupId> <artifactId>context</artifactId> <version>1.0.0.RELEASE</version> </dependency> </dependencies> 

Testklassenanwendung.

 @ScanPackage(packages = {"org.di.test"}) public class MainTest { public static void main(String... args) { IoCStarter.start(MainTest.class, args); } } 

** Erklärungen:
@ ScanPackage-Annotation - Gibt den Kontext an, welche Pakete gescannt werden sollen, um Komponenten (Klassen) für ihre Injektion zu identifizieren. Wenn das Paket nicht angegeben ist, wird das Paket der mit dieser Anmerkung gekennzeichneten Klasse gescannt.

IoCStarter # start (Object, String ...) - Einstiegspunkt und Initialisierung des Anwendungskontexts.

Darüber hinaus erstellen wir mehrere Komponentenklassen zur direkten Überprüfung der Funktionalität.

Componententa
 @IoCComponent @LoadOpt(PROTOTYPE) public class ComponentA { @Override public String toString() { return "ComponentA{" + Integer.toHexString(hashCode()) + "}"; } } 


Komponenteb
 @IoCComponent public class ComponentB { @IoCDependency private ComponentA componentA; @IoCDependency private ExampleEnvironment exampleEnvironment; @Override public String toString() { return "ComponentB{hash: " + Integer.toHexString(hashCode()) + ", componentA=" + componentA + ", exampleEnvironment=" + exampleEnvironment + '}'; } } 


Componententc
 @IoCComponent public class ComponentC { private final ComponentB componentB; private final ComponentA componentA; @IoCDependency public ComponentC(ComponentB componentB, ComponentA componentA) { this.componentB = componentB; this.componentA = componentA; } @Override public String toString() { return "ComponentC{hash: " + Integer.toHexString(hashCode()) + ", componentB=" + componentB + ", componentA=" + componentA + '}'; } } 


Componententd
 @IoCComponent public class ComponentD { @IoCDependency private ComponentB componentB; @IoCDependency private ComponentA componentA; @IoCDependency private ComponentC componentC; @Override public String toString() { return "ComponentD{hash: " + Integer.toHexString(hashCode()) + ", ComponentB=" + componentB + ", ComponentA=" + componentA + ", ComponentC=" + componentC + '}'; } } 


* Anmerkungen:
- Es werden keine zyklischen Abhängigkeiten bereitgestellt. Es gibt einen Stub in Form eines Analysators, der wiederum die empfangenen Klassen von gescannten Paketen überprüft und eine Ausnahme auslöst, wenn eine Schleife vorhanden ist.
** Erklärungen:
@ IoCComponent-Annotation - Zeigt den Kontext an, dass dies eine Komponente ist und analysiert werden muss, um Abhängigkeiten zu identifizieren (erforderliche Annotation).

Annotation @IoCDependency - Zeigt dem Analysator an, dass dies eine Komponentenabhängigkeit ist und in die Komponente instanziiert werden muss.

Annotation @LoadOpt - Zeigt den Kontext an, welche Art des Komponentenladens verwendet werden soll. Derzeit werden zwei Typen unterstützt - SINGLETON und PROTOTYPE (einzeln und mehrfach).

Erweitern wir die Implementierung der Hauptklasse:

Maintest
 @ScanPackage(packages = {"org.di.test", "org.di"}) public class MainTest extends Assert { private static final Logger log = LoggerFactory.getLogger(MainTest.class); private AppContext appContext; @Before public void initializeContext() { BasicConfigurator.configure(); appContext = IoCStarter.start(MainTest.class, (String) null); } @Test public void printStatistic() { DependencyFactory dependencyFactory = appContext.getDependencyFactory(); log.info("Initializing singleton types - {}", dependencyFactory.getSingletons().size()); log.info("Initializing proto types - {}", dependencyFactory.getPrototypes().size()); log.info("For Each singleton types"); for (Object o : dependencyFactory.getSingletons().values()) { log.info("------- {}", o.getClass().getSimpleName()); } log.info("For Each proto types"); for (Object o : dependencyFactory.getPrototypes().values()) { log.info("------- {}", o.getClass().getSimpleName()); } } @Test public void testInstantiatedComponents() { log.info("Getting ExampleEnvironment from context"); final ExampleEnvironment exampleEnvironment = appContext.getType(ExampleEnvironment.class); assertNotNull(exampleEnvironment); log.info(exampleEnvironment.toString()); log.info("Getting ComponentB from context"); final ComponentB componentB = appContext.getType(ComponentB.class); assertNotNull(componentB); log.info(componentB.toString()); log.info("Getting ComponentC from context"); final ComponentC componentC = appContext.getType(ComponentC.class); assertNotNull(componentC); log.info(componentC.toString()); log.info("Getting ComponentD from context"); final ComponentD componentD = appContext.getType(ComponentD.class); assertNotNull(componentD); log.info(componentD.toString()); } @Test public void testProto() { log.info("Getting ComponentA from context (first call)"); final ComponentA componentAFirst = appContext.getType(ComponentA.class); log.info("Getting ComponentA from context (second call)"); final ComponentA componentASecond = appContext.getType(ComponentA.class); assertNotSame(componentAFirst, componentASecond); log.info(componentAFirst.toString()); log.info(componentASecond.toString()); } @Test public void testInterfacesAndAbstracts() { log.info("Getting MyInterface from context"); final InterfaceComponent myInterface = appContext.getType(MyInterface.class); log.info(myInterface.toString()); log.info("Getting TestAbstractComponent from context"); final AbstractComponent testAbstractComponent = appContext.getType(TestAbstractComponent.class); log.info(testAbstractComponent.toString()); } } 


Wir starten das Projekt über Ihre IDE oder Befehlszeile.

Ausführungsergebnis
 Connected to the target VM, address: '127.0.0.1:55511', transport: 'socket' 0 [main] INFO org.di.context.runner.IoCStarter - Start initialization of context app 87 [main] DEBUG org.reflections.Reflections - going to scan these urls: file:/C:/Users/GenCloud/Workspace/di_container/context/target/classes/ file:/C:/Users/GenCloud/Workspace/di_container/context/target/test-classes/ [main] DEBUG org.reflections.Reflections - could not scan file log4j2.xml in url file:/C:/Users/GenCloud/Workspace/di_container/context/target/test-classes/ with scanner SubTypesScanner [main] DEBUG org.reflections.Reflections - could not scan file log4j2.xml in url file:/C:/Users/GenCloud/Workspace/di_container/context/target/test-classes/ with scanner TypeAnnotationsScanner [main] INFO org.reflections.Reflections - Reflections took 334 ms to scan 2 urls, producing 21 keys and 62 values [main] INFO org.di.context.runner.IoCStarter - App context started in [0] seconds [main] INFO org.di.test.MainTest - Initializing singleton types - 6 [main] INFO org.di.test.MainTest - Initializing proto types - 1 [main] INFO org.di.test.MainTest - For Each singleton types [main] INFO org.di.test.MainTest - ------- ComponentC [main] INFO org.di.test.MainTest - ------- TestAbstractComponent [main] INFO org.di.test.MainTest - ------- ComponentD [main] INFO org.di.test.MainTest - ------- ComponentB [main] INFO org.di.test.MainTest - ------- ExampleEnvironment [main] INFO org.di.test.MainTest - ------- MyInterface [main] INFO org.di.test.MainTest - For Each proto types [main] INFO org.di.test.MainTest - ------- ComponentA [main] INFO org.di.test.MainTest - Getting ExampleEnvironment from context [main] INFO org.di.test.MainTest - ExampleEnvironment{hash: 6f96c77, nameApp='Di Container (ver. 0.0.0.2)', components=[ComponentD, ComponentC, ComponentB, ComponentA]} [main] INFO org.di.test.MainTest - Getting ComponentB from context [main] INFO org.di.test.MainTest - ComponentB{hash: be64738, componentA=ComponentA{3ba9ad43}, exampleEnvironment=ExampleEnvironment{hash: 6f96c77, nameApp='Di Container (ver. 0.0.0.2)', components=[ComponentD, ComponentC, ComponentB, ComponentA]}} [main] INFO org.di.test.MainTest - Getting ComponentC from context [main] INFO org.di.test.MainTest - ComponentC{hash: 49d904ec, componentB=ComponentB{hash: be64738, componentA=ComponentA{3ba9ad43}, exampleEnvironment=ExampleEnvironment{hash: 6f96c77, nameApp='Di Container (ver. 0.0.0.2)', components=[ComponentD, ComponentC, ComponentB, ComponentA]}}, componentA=ComponentA{48e4374}} [main] INFO org.di.test.MainTest - Getting ComponentD from context [main] INFO org.di.test.MainTest - ComponentD{hash: 3d680b5a, ComponentB=ComponentB{hash: be64738, componentA=ComponentA{3ba9ad43}, exampleEnvironment=ExampleEnvironment{hash: 6f96c77, nameApp='Di Container (ver. 0.0.0.2)', components=[ComponentD, ComponentC, ComponentB, ComponentA]}}, ComponentA=ComponentA{4b5d6a01}, ComponentC=ComponentC{hash: 49d904ec, componentB=ComponentB{hash: be64738, componentA=ComponentA{3ba9ad43}, exampleEnvironment=ExampleEnvironment{hash: 6f96c77, nameApp='Di Container (ver. 0.0.0.2)', components=[ComponentD, ComponentC, ComponentB, ComponentA]}}, componentA=ComponentA{48e4374}}} [main] INFO org.di.test.MainTest - Getting MyInterface from context [main] INFO org.di.test.MainTest - MyInterface{componentA=ComponentA{cd3fee8}} [main] INFO org.di.test.MainTest - Getting TestAbstractComponent from context [main] INFO org.di.test.MainTest - TestAbstractComponent{componentA=ComponentA{3e2e18f2}, AbstractComponent{}} [main] INFO org.di.test.MainTest - Getting ComponentA from context (first call) [main] INFO org.di.test.MainTest - ComponentA{10e41621} [main] INFO org.di.test.MainTest - Getting ComponentA from context (second call) [main] INFO org.di.test.MainTest - ComponentA{353d0772} Disconnected from the target VM, address: '127.0.0.1:55511', transport: 'socket' Process finished with exit code 0 


+ Es gibt eine integrierte API-Analyse von Konfigurationsdateien (INI, XML, Eigenschaften).
Der Einlauftest befindet sich im Repository.

Die Zukunft


Pläne, das Projekt so weit wie möglich zu erweitern und zu unterstützen.

Was ich sehen möchte:

  1. Schreiben zusätzlicher Module - Netzwerk / Arbeiten mit Datenbanken / Schreiben von Lösungen für häufig auftretende Probleme.
  2. Ersetzen der Java Reflection API durch CGLIB
  3. usw. (Ich höre Benutzern zu, falls vorhanden)

Darauf folgt das logische Ende des Artikels.

Danke an alle. Ich hoffe, jemand findet meine Arbeit nützlich.
UPD Artikelaktualisierung - 15.09.2008. Release 1.0.0

Source: https://habr.com/ru/post/de422857/


All Articles