تنفيذ حاوية IoC الخاصة بك

الصورة

مقدمة


يجب أن يكون كل مطور مبتدئ على دراية بمفهوم عكس التحكم.

يبدأ كل مشروع جديد تقريبًا الآن باختيار إطار يتم من خلاله تطبيق مبدأ حقن التبعية.

يعد عكس التحكم (IoC) أحد المبادئ المهمة للبرمجة الموجهة للكائنات ، ويستخدم لتقليل التماسك في برامج الكمبيوتر وأحد أهم خمسة مبادئ في SOLID.

اليوم ، هناك العديد من الأطر الرئيسية حول هذا الموضوع:

1. خنجر
2. جوجل غيس
3. إطار الربيع

ما زلت أستخدم Spring وأنا راضٍ جزئيًا عن وظيفته ، ولكن حان الوقت لتجربة شيء خاص بي ، أليس كذلك؟

عن نفسي


اسمي نيكيتا ، أبلغ من العمر 24 عامًا ، وأنا أقوم بـ جافا (الخلفية) لمدة 3 سنوات. درس فقط مع أمثلة عملية ، بينما كان يحاول في نفس الوقت فهم بقايا الطبقات. أعمل حاليًا (لحسابهم الخاص) - كتابة CMS لمشروع تجاري ، حيث أستخدم Spring Boot. قمت مؤخرًا بزيارة الفكرة - "لماذا لا تكتب حاوية IoC (DI) وفقًا لرؤيتك ورغبتك؟". تحدث تقريبا - "أردت بلدي مع لعبة ورق ...". سيتم مناقشة هذا اليوم. حسنا ، من فضلك ، تحت القط. رابط لمصادر المشروع .

الميزات


- السمة الرئيسية للمشروع هي حقن التبعية.
يتم دعم 3 طرق حقن تبعية رئيسية:
  1. حقول الصف
  2. مُنشئ الصف
  3. وظائف الفئة (محدد قياسي لمعلمة واحدة)

* ملاحظة:
- عند مسح فئة ، إذا تم استخدام جميع طرق الحقن الثلاثة في وقت واحد ، فإن طريقة الحقن من خلال مُنشئ الفئة التي تم وضع علامة عليها على التعليق التوضيحي لـIoCDependency ستكون ذات أولوية. على سبيل المثال طريقة حقن واحدة فقط تعمل دائمًا.

- التهيئة البطيئة للمكونات (عند الطلب) ؛

- ملفات تكوين محمل مضمنة (التنسيقات: ini ، xml ، الخصائص) ؛

- معالج وسيطة سطر الأوامر ؛

- وحدات المعالجة عن طريق إنشاء المصانع ؛

- الأحداث المدمجة والمستمعين ؛

- مخبرون مدمجون (Sensibles) من أجل "إبلاغ" مكون أو مصنع أو مستمع أو معالج (ComponentProcessor) بأنه يجب تحميل معلومات معينة في الكائن ، اعتمادًا على المخبر ؛

- وحدة لإدارة / إنشاء مجموعة من مؤشرات الترابط ، إعلان الوظائف كمهام قابلة للتنفيذ لبعض الوقت وتهيئتها في مصنع التجمع ، وكذلك البدء بمعلمات SimpleTask.

كيف يحدث مسح الحزم:
ويستخدم واجهة برمجة تطبيقات Reflections لجهة خارجية مع ماسح ضوئي قياسي.

//{@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; } 

نحصل على مجموعة من الفئات باستخدام مرشحات التعليقات التوضيحية والأنواع.
في هذه الحالة ، هذه هيIoCComponent وProperty and progenitor Analyzer <R، T>

ترتيب تهيئة السياق:
1) بادئ ذي بدء ، تتم تهيئة أنواع التكوين.
 //{@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); } } } 

* التفسيرات:
يحتوي التعليق التوضيحيProperty على معلمة سلسلة مطلوبة - المسار (المسار إلى ملف التكوين). هذا هو المكان الذي يتم فيه البحث عن الملف لتحليل التكوين.
الفئة PropertiesLoader هي فئة أداة مساعدة لتهيئة حقول الفئة المقابلة لحقول ملف التكوين.
Function DependencyFactory # addInstalledConfiguration (Object) - يتم تحميل كائن التكوين في المصنع كـ SINGLETON (وإلا فمن المنطقي إعادة تشغيل التكوين وليس عند الطلب).

2) تهيئة المحللين
3) تهيئة المكونات التي تم العثور عليها (الفئات المميزة بعلامةIoCComponent)
 //{@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); } 

* التفسيرات:
فئة ClassAnalyzer - تحدد طريقة حقن التبعية ، وأيضًا إذا كانت هناك أخطاء في الموضع غير الصحيح للتعليقات التوضيحية وإعلانات المُنشئ والمعلمات في الطريقة - تُرجع خطأً. محلل الدوال <R، T> #analyze (T) - إرجاع نتيجة التحليل. محلل الدوال <R، T> #supportFor (T) - يُرجع معلمة منطقية اعتمادًا على الشروط المحددة.
Function DependencyFactory # Instantiate (الفئة ، R) - يقوم بتثبيت النوع في المصنع باستخدام الطريقة المحددة بواسطة ClassAnalyzer أو يطرح استثناء إذا كانت هناك أخطاء إما في التحليل أو في عملية تهيئة الكائن.

3) طرق المسح
- طريقة حقن المعلمات في مُنشئ الصنف
  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; } 

- طريقة حقن المعلمات في الحقول الطبقية
  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); } } 

- طريقة حقن المعلمات من خلال وظائف الصنف
  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); } } 



واجهة برمجة تطبيقات المستخدم
1. ComponentProcessor - أداة مساعدة تسمح لك بتغيير المكون كما تشاء ، قبل التهيئة في السياق وبعده.
 public interface ComponentProcessor { Object afterComponentInitialization(String componentName, Object component); Object beforeComponentInitialization(String componentName, Object component); } 


* التفسيرات:
الوظيفة #afterComponentInitialization (سلسلة ، كائن) - تسمح لك بمعالجة المكون بعد تهيئته في السياق ، المعلمات الواردة - (اسم ثابت للمكون ، كائن فوري للمكون).
الوظيفة #beforeComponentInitialization (سلسلة ، كائن) - تسمح لك بمعالجة المكون قبل تهيئته في السياق ، المعلمات الواردة - (اسم ثابت للمكون ، كائن فوري للمكون).

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


* التفسيرات:
الوظيفة #resolve (String ...) هي واجهة تعالج الأوامر المختلفة المرسلة من خلال cmd عند بدء تشغيل التطبيق ، ومعلمة الإدخال عبارة عن مجموعة غير محدودة من سلاسل سطر الأوامر (المعلمات).
3. المخبرين (Sensibles) - يشير إلى أن فئة الطفل من المخبر ستحتاج إلى تضمين opr. الوظيفة حسب نوع المخبر (ContextSensible و EnvironmentSensible و ThreadFactorySensible وما إلى ذلك)

4. المستمعين
يتم تنفيذ وظيفة المستمعين ، ويضمن التنفيذ متعدد الخيوط مع العدد الموصى به من الواصفات التي تم تكوينها للأحداث المحسنة.
 @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; } } 

** التفسيرات:
وظيفة الإرسال (الحدث) هي الوظيفة الرئيسية لمعالج أحداث النظام.
- هناك تطبيقات قياسية للمستمعين مع التحقق من أنواع الأحداث وكذلك مع فلاتر المستخدم المضمنة {@ link Filter}. الفلاتر القياسية المضمنة في الحزمة: AndFilter و ExcludeFilter و NotFilter و OrFilter و InstanceFilter (custom). تطبيقات المستمع القياسية: FilteredListener و TypedListener. يستخدم الأول عامل تصفية للتحقق من كائن الحدث الوارد. يقوم الثاني بفحص كائن الحدث أو أي شيء آخر للانتماء إلى مثيل محدد.



الوحدات
1) وحدة للعمل مع مهام الدفق في التطبيق الخاص بك

- ربط التبعيات
 <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> 


- علامة تعليق لتضمين الوحدة في السياق (ThreadingModule)
 @ThreadingModule @ScanPackage(packages = {"org.di.test"}) public class MainTest { public static void main(String... args){ IoCStarter.start(MainTest.class, args); } } 


- تنفيذ مصنع الوحدة في المكون المثبت في التطبيق
 @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()); } } 

* التفسيرات:
ThreadFactorySensible هي واحدة من فئات المخبرين الأطفال للتنفيذ في المكون الفوري للمساعدة الإنمائية الرسمية. المعلومات (التكوين ، السياق ، الوحدة النمطية ، إلخ).
DefaultThreadingFactory - مصنع وحدة التخييط-المصنع.

التعليق التوضيحيSimpleTask هو تعليق توضيحي محدد للمعلمات لتحديد تنفيذ المكون للمهام في الوظائف. (يبدأ الدفق بالمعلمات المحددة مع تعليق توضيحي ويضيفه إلى المصنع ، حيث يمكن الحصول عليه ، وعلى سبيل المثال ، تعطيل التنفيذ).

- وظائف تسلسل المهام القياسية
  //   . ,  ,       . <T> AsyncFuture<T> async(Task<T>) //      . <T> AsyncFuture<T> async(long, TimeUnit, Task<T>) //      . ScheduledAsyncFuture async(long, TimeUnit, long, Runnable) 


*** يرجى ملاحظة أن الموارد الموجودة في تجمع سلاسل المحادثات المجدولة محدودة ويجب إكمال المهام بسرعة.

- تكوين التجمع الافتراضي
 # Threading threads.poolName=shared threads.availableProcessors=4 threads.threadTimeout=0 threads.threadAllowCoreTimeOut=true threads.threadPoolPriority=NORMAL 




نقطة البداية أو كيف يعمل كل شيء


نحن نربط تبعيات المشروع:

  <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> 

تطبيق فئة الاختبار.

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

** التفسيرات:
التعليق التوضيحي لـ ScanPackage - يخبر السياق بالحزم التي يجب فحصها لتحديد المكونات (الفئات) لحقنها . إذا لم يتم تحديد حزمة ، فسيتم فحص حزمة الفئة المميزة بهذا التعليق التوضيحي.

IoCStarter # start (كائن ، سلسلة ...) - نقطة الدخول وتهيئة سياق التطبيق.

بالإضافة إلى ذلك ، سنقوم بإنشاء العديد من فئات المكونات للتحقق مباشرة من الوظائف.

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


المكون ب
 @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 + '}'; } } 


المكون ج
 @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 + '}'; } } 


مكون د
 @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 + '}'; } } 


* ملاحظات:
- لا يتم توفير التبعيات الدورية ، يوجد كعب على شكل محلل ، والذي بدوره يقوم بفحص الفئات المستلمة من الحزم الممسوحة ضوئيًا ويلقي استثناء إذا كان هناك حلقة.
** التفسيرات:
IoCComponent التعليقات التوضيحية - يوضح السياق أن هذا مكون ويحتاج إلى تحليل لتحديد التبعيات (التعليق التوضيحي المطلوب).

التعليق التوضيحيIoCDependency - يظهر للمحلل أن هذا تبعية للمكون ويجب نسخه إلى المكون.

تعليق توضيحيLoadOpt - يوضح السياق نوع تحميل المكون الذي يجب استخدامه. حاليًا ، يتم دعم نوعين - SINGLETON و PROTOTYPE (فردي ومتعدد).

دعنا نوسع تطبيق الفئة الرئيسية:

صيانة
 @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()); } } 


نبدأ المشروع باستخدام IDE أو سطر الأوامر.

نتيجة التنفيذ
 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 


+ هناك تحليل واجهة برمجة التطبيقات المضمنة لملفات التكوين (ini ، xml ، الخصائص).
اختبار التشغيل في المستودع.

المستقبل


خطط لتوسيع ودعم المشروع قدر الإمكان.

ما أريد رؤيته:

  1. كتابة وحدات إضافية - شبكة / العمل مع قواعد البيانات / حلول الكتابة للمشكلات الشائعة.
  2. استبدال واجهة برمجة تطبيقات Java Reflection API بـ CGLIB
  3. الخ. (أستمع إلى المستخدمين ، إن وجد)

ويلي ذلك نهاية منطقية للمقال.

شكرا لكم جميعا. آمل أن يجد شخص ما عملي مفيدًا.
UPD تحديث المادة - 09/15/2018. الإصدار 1.0.0

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


All Articles