From e673232ab5673d9e29f59911ed282ceea78d2c9b Mon Sep 17 00:00:00 2001 From: JulianDev <61058379+JulianDev24@users.noreply.github.com> Date: Tue, 14 Feb 2023 03:20:41 +0800 Subject: [PATCH 1/5] Implement Dynamic Module Registration --- .../juliandev/easy/injector/EasyInjector.java | 4 ++ .../easy/injector/EasyInjectorImpl.java | 12 +++++ .../xyz/juliandev/easy/DynamicModuleTest.java | 45 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 core/src/test/java/xyz/juliandev/easy/DynamicModuleTest.java diff --git a/core/src/main/java/xyz/juliandev/easy/injector/EasyInjector.java b/core/src/main/java/xyz/juliandev/easy/injector/EasyInjector.java index 77ef071..32b57ea 100644 --- a/core/src/main/java/xyz/juliandev/easy/injector/EasyInjector.java +++ b/core/src/main/java/xyz/juliandev/easy/injector/EasyInjector.java @@ -1,5 +1,7 @@ package xyz.juliandev.easy.injector; +import xyz.juliandev.easy.module.AbstractModule; + public interface EasyInjector { /** @@ -24,4 +26,6 @@ public interface EasyInjector { boolean injectObjectFields(Object target); + void addDynamicModule(AbstractModule abstractModule); + } diff --git a/core/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java b/core/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java index 84205a6..2399edc 100644 --- a/core/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java +++ b/core/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java @@ -26,9 +26,11 @@ public EasyInjectorImpl(Iterable modules) { providerMethod(module, providerMethod); } } + } + /** * @return an instance of type */ @@ -61,6 +63,16 @@ public Provider getProvider(Key key) { return provider(key, null); } + /** + * Dynamic Registers AbstractModules + */ + @Override + public void addDynamicModule(AbstractModule abstractModule){ + for (Method providerMethod : providers(abstractModule.getClass())) { + providerMethod(abstractModule, providerMethod); + } + } + /** * Injects fields to the target object */ diff --git a/core/src/test/java/xyz/juliandev/easy/DynamicModuleTest.java b/core/src/test/java/xyz/juliandev/easy/DynamicModuleTest.java new file mode 100644 index 0000000..96966e8 --- /dev/null +++ b/core/src/test/java/xyz/juliandev/easy/DynamicModuleTest.java @@ -0,0 +1,45 @@ +package xyz.juliandev.easy; + +import org.junit.Test; +import xyz.juliandev.easy.annotations.Inject; +import xyz.juliandev.easy.annotations.Named; +import xyz.juliandev.easy.annotations.Provides; +import xyz.juliandev.easy.injector.EasyInjector; +import xyz.juliandev.easy.module.AbstractModule; + +public class DynamicModuleTest { + + @Test + public void dynamicModule() { + EasyInjector injector = Easy.createInjector(new AbstractModule() { + @Provides + @Named("foo") + String foo() { + return "foo"; + } + }); + injector.addDynamicModule(new AbstractModule() { + + @Provides + @Named("bar") + String bar() { + return "bar"; + } + }); + + TestObj obj = injector.getInstance(TestObj.class); + obj.print(); + + + } + + public record TestObj(@Named("foo") String foo, @Named("bar") String bar) { + @Inject + public TestObj {} + + public void print() { + System.out.println("fooVar: " + foo + " barVar: " + bar); + } + } + +} From db5a6d77de28c03b130e3e420a3733fc9cb4f2c8 Mon Sep 17 00:00:00 2001 From: JulianDev <61058379+JulianDev24@users.noreply.github.com> Date: Tue, 14 Feb 2023 03:21:28 +0800 Subject: [PATCH 2/5] Update Version --- core/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index ab3dff6..e94d894 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,7 @@ parent xyz.juliandev - 1.0.0 + 1.1.0 4.0.0 diff --git a/pom.xml b/pom.xml index 5182427..8a8a432 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ xyz.juliandev parent pom - 1.0.0 + 1.1.0 core From 7e836fe5016facf4001690a884788a408a89dca0 Mon Sep 17 00:00:00 2001 From: JulianDev24 Date: Mon, 3 Jul 2023 12:30:40 +0800 Subject: [PATCH 3/5] Update Project strcuture --- LICENSE | 13 - README.md | 229 ------------------ core/pom.xml | 15 -- pom.xml | 16 +- .../main/java/xyz/juliandev/easy/Easy.java | 0 .../juliandev/easy/annotations/Inject.java | 0 .../xyz/juliandev/easy/annotations/Named.java | 0 .../juliandev/easy/annotations/Provides.java | 0 .../juliandev/easy/annotations/Qualifier.java | 0 .../xyz/juliandev/easy/annotations/Scope.java | 7 +- .../juliandev/easy/annotations/Singleton.java | 0 .../juliandev/easy/injector/EasyInjector.java | 0 .../easy/injector/EasyInjectorImpl.java | 3 +- .../EasyInjectorRuntimeException.java | 0 .../java/xyz/juliandev/easy/injector/Key.java | 0 .../xyz/juliandev/easy/injector/Provider.java | 0 .../juliandev/easy/module/AbstractModule.java | 0 .../juliandev/easy/AmbiguousModuleTest.java | 0 .../xyz/juliandev/easy/DynamicModuleTest.java | 0 .../juliandev/easy/FieldInjectionTest.java | 0 .../juliandev/easy/ModuleOverrideTest.java | 0 .../juliandev/easy/NamedDependencyTest.java | 0 .../easy/PolymorphicDependencyTest.java | 0 .../juliandev/easy/ProviderInjectionTest.java | 0 .../xyz/juliandev/easy/SingletonTest.java | 2 +- 25 files changed, 11 insertions(+), 274 deletions(-) delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 core/pom.xml rename {core/src => src}/main/java/xyz/juliandev/easy/Easy.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/annotations/Inject.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/annotations/Named.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/annotations/Provides.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/annotations/Qualifier.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/annotations/Scope.java (99%) rename {core/src => src}/main/java/xyz/juliandev/easy/annotations/Singleton.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/injector/EasyInjector.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java (99%) rename {core/src => src}/main/java/xyz/juliandev/easy/injector/EasyInjectorRuntimeException.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/injector/Key.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/injector/Provider.java (100%) rename {core/src => src}/main/java/xyz/juliandev/easy/module/AbstractModule.java (100%) rename {core/src => src}/test/java/xyz/juliandev/easy/AmbiguousModuleTest.java (100%) rename {core/src => src}/test/java/xyz/juliandev/easy/DynamicModuleTest.java (100%) rename {core/src => src}/test/java/xyz/juliandev/easy/FieldInjectionTest.java (100%) rename {core/src => src}/test/java/xyz/juliandev/easy/ModuleOverrideTest.java (100%) rename {core/src => src}/test/java/xyz/juliandev/easy/NamedDependencyTest.java (100%) rename {core/src => src}/test/java/xyz/juliandev/easy/PolymorphicDependencyTest.java (100%) rename {core/src => src}/test/java/xyz/juliandev/easy/ProviderInjectionTest.java (100%) rename {core/src => src}/test/java/xyz/juliandev/easy/SingletonTest.java (100%) diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 19c1323..0000000 --- a/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright {2023} {JulianDev24} - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index ac62d43..0000000 --- a/README.md +++ /dev/null @@ -1,229 +0,0 @@ -#### About EasyInject -[EasyInject](https://github.com/JulianDev24/EasyInject) is a lightweight dependency injection framework for Java. -It was originally developed for Minecraft to have a lightweight system, but it can also be used for general Java and Android applications. -EasyInject allows developers to instantiate dependencies, provide additional dependencies, and inject dependencies into fields or constructors of other classes. - -```xml - - - - jitpack.io - https://jitpack.io - - - - - - com.github.JulianDev24.EasyInject - easyinjector-core - 1.0.0 - - -``` - -##### Usage - code examples -###### Create EasyInjector (the injector) -```java -EasyInjector easyInject = Easy.createInjector(); -``` -An application typically needs a single EasyInjector instance. - -###### Instantiating dependencies -EasyInject allows for the injection of dependencies with either an @Inject annotated constructor or a default constructor without the need for any configuration. - -For example: -```java -public class A { - @Inject - public A(B b) { - // ... - } -} - -public class B { - @Inject - public B(C c, D d) { - // ... - } -} - -public class C {} - -@Singleton -public class D { - // something expensive or other reasons for being singleton -} -``` -Creating an instance of A: -```java -A a = easyInject.getInstance(A.class); -``` -###### Providing additional dependencies to EasyInject -EasyInject relies on configuration modules to provide dependencies for interfaces, third-party classes, or objects that require custom instantiation. -```java -public class MyModule { - @Provides - @Singleton // an app will probably need a single instance - DataSource ds() { - DataSource dataSource = // instantiate some DataSource - return dataSource; - } -} -``` - -Setting up EasyInject with module(s): - -```java -EasyInjector easyInject = Easy.createInjector(new MyModule()); -``` -The DataSource dependency will now be available for injection: -```java -public class MyApp { - @Inject - public MyApp(DataSource ds) { - // ... - } -} -``` -EasyInject injects dependencies into arguments of @Provides methods, making it especially useful for linking an implementation to an interface: -```java -public interface Foo {} - -public class FooBar implements Foo { - @Inject - public FooBar(X x, Y y, Z z) { - // ... - } -} - -public class MyModule { - @Provides - Foo foo(FooBar fooBar) { - return fooBar; - } -} - -// injecting an instance of Foo interface will work using the MyModule above: -public class A { - @Inject - public A(Foo foo) { - // ... - } -} -``` -EasyInject eliminates the need for manual instantiation by utilizing the @Provides method as a binding declaration. -###### Qualifiers -EasyInject accommodates the use of Qualifiers, such as @Named or custom qualifiers. -```java -public class MyModule { - @Provides - @Named("greeting") - String greeting() { - return "hi"; - } - - @Provides - @SomeQualifier - Foo some(FooSome fooSome) { - return fooSome; - } -} -``` -Injecting: -```java -public class A { - @Inject - public A(@SomeQualifier Foo foo, @Named("greeting") String greet) { - // ... - } -} -``` -Or directly from easyInject: -```java -String greet = easyInject.getInstance(String.class, "greeting"); -Foo foo = easyInject.getInstance(Key.of(Foo.class, SomeQualifier.class)); -``` -###### Provider injection -EasyInject utilizes [Provider](https://docs.oracle.com/javaee/6/api/javax/inject/Provider.html)s to support lazy loading and manage circular dependencies: -```java -public class A { - @Inject - public A(Provider b) { - B b = b.get(); // fetch a new instance when needed - } -} -``` -Or getting a Provider directly from EasyInject: -```java -Provider bProvider = easyInject.getProvider(B.class); -``` -###### Override modules -```java -public class Module { - @Provides - DataSource dataSource() { - // return a mysql datasource - } - - // other @Provides methods -} - -public class TestModule extends Module { - @Override - @Provides - DataSource dataSource() { - // return a h2 datasource - } -} -``` -###### Field injection -EasyInject primarily utilizes Constructor Injection when injecting within a dependency graph. However, -it also supports Field Injection if explicitly prompted for a specific target object, such as during testing. -A simple example of this can be seen in a JUnit test: -```java -public class AUnitTest { - @Inject - private Foo foo; - @Inject - private Bar bar; - - @Before - public void setUp() { - EasyInjector easyInjector = // obtain a EasyInjector instance - easyInjector.injectObjectFields(this); - } -} -``` -###### Method injection -EasyInject does not support Field injection as a general practice. -It can be avoided by using Providers and designing objects with immutability in mind, with dependencies injected through the constructor. - -##### How it works under the hood -EasyInject utilizes reflection optimally to deliver dependencies without incurring any cost-intensive processes such as code generation, -classpath scanning, or proxying. - -A simple example with some explanation: -```java -class A { - @Inject - A(B b) { - - } -} - -class B { - -} -``` -Without the use of EasyInject, class A could be instantiated with the following factory methods: -```java -A a() { - return new A(b()); -} - -B b() { - return new B(); -} -``` -EasyInject eliminates the need for factories and reduces the risk of changes causing merge conflicts. -It does this by using reflection to create instances, calling a class's constructor with necessary dependencies and repeating the process for each subsequent dependency. diff --git a/core/pom.xml b/core/pom.xml deleted file mode 100644 index e94d894..0000000 --- a/core/pom.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - parent - xyz.juliandev - 1.1.0 - - 4.0.0 - - easyinjector-core - - EasyInjectorCore - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8a8a432..0de54f5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,15 +5,8 @@ 4.0.0 xyz.juliandev - parent - pom - 1.1.0 - - - core - - - EasyInjector + EasyInject + 1.0.2 EasyInjector is an ultra-lightweight dependency injection library for Java. @@ -22,7 +15,7 @@ The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + https://www.apache.org/licenses/LICENSE-2.0.txt repo @@ -77,7 +70,7 @@ maven-jar-plugin 3.2.0 - ${project.parent.basedir}/artifacts/ + ${project.basedir}/artifacts/ @@ -112,4 +105,5 @@ + \ No newline at end of file diff --git a/core/src/main/java/xyz/juliandev/easy/Easy.java b/src/main/java/xyz/juliandev/easy/Easy.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/Easy.java rename to src/main/java/xyz/juliandev/easy/Easy.java diff --git a/core/src/main/java/xyz/juliandev/easy/annotations/Inject.java b/src/main/java/xyz/juliandev/easy/annotations/Inject.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/annotations/Inject.java rename to src/main/java/xyz/juliandev/easy/annotations/Inject.java diff --git a/core/src/main/java/xyz/juliandev/easy/annotations/Named.java b/src/main/java/xyz/juliandev/easy/annotations/Named.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/annotations/Named.java rename to src/main/java/xyz/juliandev/easy/annotations/Named.java diff --git a/core/src/main/java/xyz/juliandev/easy/annotations/Provides.java b/src/main/java/xyz/juliandev/easy/annotations/Provides.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/annotations/Provides.java rename to src/main/java/xyz/juliandev/easy/annotations/Provides.java diff --git a/core/src/main/java/xyz/juliandev/easy/annotations/Qualifier.java b/src/main/java/xyz/juliandev/easy/annotations/Qualifier.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/annotations/Qualifier.java rename to src/main/java/xyz/juliandev/easy/annotations/Qualifier.java diff --git a/core/src/main/java/xyz/juliandev/easy/annotations/Scope.java b/src/main/java/xyz/juliandev/easy/annotations/Scope.java similarity index 99% rename from core/src/main/java/xyz/juliandev/easy/annotations/Scope.java rename to src/main/java/xyz/juliandev/easy/annotations/Scope.java index 17e0ef6..dd2f9b6 100644 --- a/core/src/main/java/xyz/juliandev/easy/annotations/Scope.java +++ b/src/main/java/xyz/juliandev/easy/annotations/Scope.java @@ -1,10 +1,11 @@ package xyz.juliandev.easy.annotations; -import java.lang.annotation.Target; -import java.lang.annotation.Retention; import java.lang.annotation.Documented; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target(ANNOTATION_TYPE) @Retention(RUNTIME) diff --git a/core/src/main/java/xyz/juliandev/easy/annotations/Singleton.java b/src/main/java/xyz/juliandev/easy/annotations/Singleton.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/annotations/Singleton.java rename to src/main/java/xyz/juliandev/easy/annotations/Singleton.java diff --git a/core/src/main/java/xyz/juliandev/easy/injector/EasyInjector.java b/src/main/java/xyz/juliandev/easy/injector/EasyInjector.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/injector/EasyInjector.java rename to src/main/java/xyz/juliandev/easy/injector/EasyInjector.java diff --git a/core/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java b/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java similarity index 99% rename from core/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java rename to src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java index 2399edc..b85f27e 100644 --- a/core/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java +++ b/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java @@ -1,11 +1,10 @@ package xyz.juliandev.easy.injector; import xyz.juliandev.easy.annotations.Inject; +import xyz.juliandev.easy.annotations.Provides; import xyz.juliandev.easy.annotations.Qualifier; import xyz.juliandev.easy.annotations.Singleton; import xyz.juliandev.easy.module.AbstractModule; -import xyz.juliandev.easy.annotations.Provides; - import java.lang.annotation.Annotation; import java.lang.reflect.*; diff --git a/core/src/main/java/xyz/juliandev/easy/injector/EasyInjectorRuntimeException.java b/src/main/java/xyz/juliandev/easy/injector/EasyInjectorRuntimeException.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/injector/EasyInjectorRuntimeException.java rename to src/main/java/xyz/juliandev/easy/injector/EasyInjectorRuntimeException.java diff --git a/core/src/main/java/xyz/juliandev/easy/injector/Key.java b/src/main/java/xyz/juliandev/easy/injector/Key.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/injector/Key.java rename to src/main/java/xyz/juliandev/easy/injector/Key.java diff --git a/core/src/main/java/xyz/juliandev/easy/injector/Provider.java b/src/main/java/xyz/juliandev/easy/injector/Provider.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/injector/Provider.java rename to src/main/java/xyz/juliandev/easy/injector/Provider.java diff --git a/core/src/main/java/xyz/juliandev/easy/module/AbstractModule.java b/src/main/java/xyz/juliandev/easy/module/AbstractModule.java similarity index 100% rename from core/src/main/java/xyz/juliandev/easy/module/AbstractModule.java rename to src/main/java/xyz/juliandev/easy/module/AbstractModule.java diff --git a/core/src/test/java/xyz/juliandev/easy/AmbiguousModuleTest.java b/src/test/java/xyz/juliandev/easy/AmbiguousModuleTest.java similarity index 100% rename from core/src/test/java/xyz/juliandev/easy/AmbiguousModuleTest.java rename to src/test/java/xyz/juliandev/easy/AmbiguousModuleTest.java diff --git a/core/src/test/java/xyz/juliandev/easy/DynamicModuleTest.java b/src/test/java/xyz/juliandev/easy/DynamicModuleTest.java similarity index 100% rename from core/src/test/java/xyz/juliandev/easy/DynamicModuleTest.java rename to src/test/java/xyz/juliandev/easy/DynamicModuleTest.java diff --git a/core/src/test/java/xyz/juliandev/easy/FieldInjectionTest.java b/src/test/java/xyz/juliandev/easy/FieldInjectionTest.java similarity index 100% rename from core/src/test/java/xyz/juliandev/easy/FieldInjectionTest.java rename to src/test/java/xyz/juliandev/easy/FieldInjectionTest.java diff --git a/core/src/test/java/xyz/juliandev/easy/ModuleOverrideTest.java b/src/test/java/xyz/juliandev/easy/ModuleOverrideTest.java similarity index 100% rename from core/src/test/java/xyz/juliandev/easy/ModuleOverrideTest.java rename to src/test/java/xyz/juliandev/easy/ModuleOverrideTest.java diff --git a/core/src/test/java/xyz/juliandev/easy/NamedDependencyTest.java b/src/test/java/xyz/juliandev/easy/NamedDependencyTest.java similarity index 100% rename from core/src/test/java/xyz/juliandev/easy/NamedDependencyTest.java rename to src/test/java/xyz/juliandev/easy/NamedDependencyTest.java diff --git a/core/src/test/java/xyz/juliandev/easy/PolymorphicDependencyTest.java b/src/test/java/xyz/juliandev/easy/PolymorphicDependencyTest.java similarity index 100% rename from core/src/test/java/xyz/juliandev/easy/PolymorphicDependencyTest.java rename to src/test/java/xyz/juliandev/easy/PolymorphicDependencyTest.java diff --git a/core/src/test/java/xyz/juliandev/easy/ProviderInjectionTest.java b/src/test/java/xyz/juliandev/easy/ProviderInjectionTest.java similarity index 100% rename from core/src/test/java/xyz/juliandev/easy/ProviderInjectionTest.java rename to src/test/java/xyz/juliandev/easy/ProviderInjectionTest.java diff --git a/core/src/test/java/xyz/juliandev/easy/SingletonTest.java b/src/test/java/xyz/juliandev/easy/SingletonTest.java similarity index 100% rename from core/src/test/java/xyz/juliandev/easy/SingletonTest.java rename to src/test/java/xyz/juliandev/easy/SingletonTest.java index ebb606d..ae875fd 100644 --- a/core/src/test/java/xyz/juliandev/easy/SingletonTest.java +++ b/src/test/java/xyz/juliandev/easy/SingletonTest.java @@ -1,9 +1,9 @@ package xyz.juliandev.easy; +import org.junit.Test; import xyz.juliandev.easy.annotations.Singleton; import xyz.juliandev.easy.injector.EasyInjector; import xyz.juliandev.easy.injector.Provider; -import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; From a373913f77f906309511c006bbb2664c2c00b7a2 Mon Sep 17 00:00:00 2001 From: JulianDev24 Date: Mon, 3 Jul 2023 12:31:41 +0800 Subject: [PATCH 4/5] Add README and License --- LICENSE | 13 ++++ README.md | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..19c1323 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright {2023} {JulianDev24} + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ac62d43 --- /dev/null +++ b/README.md @@ -0,0 +1,229 @@ +#### About EasyInject +[EasyInject](https://github.com/JulianDev24/EasyInject) is a lightweight dependency injection framework for Java. +It was originally developed for Minecraft to have a lightweight system, but it can also be used for general Java and Android applications. +EasyInject allows developers to instantiate dependencies, provide additional dependencies, and inject dependencies into fields or constructors of other classes. + +```xml + + + + jitpack.io + https://jitpack.io + + + + + + com.github.JulianDev24.EasyInject + easyinjector-core + 1.0.0 + + +``` + +##### Usage - code examples +###### Create EasyInjector (the injector) +```java +EasyInjector easyInject = Easy.createInjector(); +``` +An application typically needs a single EasyInjector instance. + +###### Instantiating dependencies +EasyInject allows for the injection of dependencies with either an @Inject annotated constructor or a default constructor without the need for any configuration. + +For example: +```java +public class A { + @Inject + public A(B b) { + // ... + } +} + +public class B { + @Inject + public B(C c, D d) { + // ... + } +} + +public class C {} + +@Singleton +public class D { + // something expensive or other reasons for being singleton +} +``` +Creating an instance of A: +```java +A a = easyInject.getInstance(A.class); +``` +###### Providing additional dependencies to EasyInject +EasyInject relies on configuration modules to provide dependencies for interfaces, third-party classes, or objects that require custom instantiation. +```java +public class MyModule { + @Provides + @Singleton // an app will probably need a single instance + DataSource ds() { + DataSource dataSource = // instantiate some DataSource + return dataSource; + } +} +``` + +Setting up EasyInject with module(s): + +```java +EasyInjector easyInject = Easy.createInjector(new MyModule()); +``` +The DataSource dependency will now be available for injection: +```java +public class MyApp { + @Inject + public MyApp(DataSource ds) { + // ... + } +} +``` +EasyInject injects dependencies into arguments of @Provides methods, making it especially useful for linking an implementation to an interface: +```java +public interface Foo {} + +public class FooBar implements Foo { + @Inject + public FooBar(X x, Y y, Z z) { + // ... + } +} + +public class MyModule { + @Provides + Foo foo(FooBar fooBar) { + return fooBar; + } +} + +// injecting an instance of Foo interface will work using the MyModule above: +public class A { + @Inject + public A(Foo foo) { + // ... + } +} +``` +EasyInject eliminates the need for manual instantiation by utilizing the @Provides method as a binding declaration. +###### Qualifiers +EasyInject accommodates the use of Qualifiers, such as @Named or custom qualifiers. +```java +public class MyModule { + @Provides + @Named("greeting") + String greeting() { + return "hi"; + } + + @Provides + @SomeQualifier + Foo some(FooSome fooSome) { + return fooSome; + } +} +``` +Injecting: +```java +public class A { + @Inject + public A(@SomeQualifier Foo foo, @Named("greeting") String greet) { + // ... + } +} +``` +Or directly from easyInject: +```java +String greet = easyInject.getInstance(String.class, "greeting"); +Foo foo = easyInject.getInstance(Key.of(Foo.class, SomeQualifier.class)); +``` +###### Provider injection +EasyInject utilizes [Provider](https://docs.oracle.com/javaee/6/api/javax/inject/Provider.html)s to support lazy loading and manage circular dependencies: +```java +public class A { + @Inject + public A(Provider b) { + B b = b.get(); // fetch a new instance when needed + } +} +``` +Or getting a Provider directly from EasyInject: +```java +Provider bProvider = easyInject.getProvider(B.class); +``` +###### Override modules +```java +public class Module { + @Provides + DataSource dataSource() { + // return a mysql datasource + } + + // other @Provides methods +} + +public class TestModule extends Module { + @Override + @Provides + DataSource dataSource() { + // return a h2 datasource + } +} +``` +###### Field injection +EasyInject primarily utilizes Constructor Injection when injecting within a dependency graph. However, +it also supports Field Injection if explicitly prompted for a specific target object, such as during testing. +A simple example of this can be seen in a JUnit test: +```java +public class AUnitTest { + @Inject + private Foo foo; + @Inject + private Bar bar; + + @Before + public void setUp() { + EasyInjector easyInjector = // obtain a EasyInjector instance + easyInjector.injectObjectFields(this); + } +} +``` +###### Method injection +EasyInject does not support Field injection as a general practice. +It can be avoided by using Providers and designing objects with immutability in mind, with dependencies injected through the constructor. + +##### How it works under the hood +EasyInject utilizes reflection optimally to deliver dependencies without incurring any cost-intensive processes such as code generation, +classpath scanning, or proxying. + +A simple example with some explanation: +```java +class A { + @Inject + A(B b) { + + } +} + +class B { + +} +``` +Without the use of EasyInject, class A could be instantiated with the following factory methods: +```java +A a() { + return new A(b()); +} + +B b() { + return new B(); +} +``` +EasyInject eliminates the need for factories and reduces the risk of changes causing merge conflicts. +It does this by using reflection to create instances, calling a class's constructor with necessary dependencies and repeating the process for each subsequent dependency. From 44df68a9776564f0bb227e9a63866bc16e230abf Mon Sep 17 00:00:00 2001 From: JulianDev24 Date: Mon, 3 Jul 2023 12:37:42 +0800 Subject: [PATCH 5/5] Removed Futures, add Dynamic Modules and bump version --- pom.xml | 4 +- .../easy/injector/EasyInjectorImpl.java | 53 +++++++------------ 2 files changed, 20 insertions(+), 37 deletions(-) diff --git a/pom.xml b/pom.xml index 0de54f5..1d0477b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ xyz.juliandev EasyInject - 1.0.2 + 2.0.0 EasyInjector is an ultra-lightweight dependency injection library for Java. @@ -60,7 +60,7 @@ org.projectlombok lombok - 1.18.26 + 1.18.28 diff --git a/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java b/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java index b85f27e..6c074bf 100644 --- a/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java +++ b/src/main/java/xyz/juliandev/easy/injector/EasyInjectorImpl.java @@ -9,11 +9,9 @@ import java.lang.annotation.Annotation; import java.lang.reflect.*; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; -public final class EasyInjectorImpl implements EasyInjector{ +public final class EasyInjectorImpl implements EasyInjector { private final Map, Provider> providers = new ConcurrentHashMap<>(); private final Map, Object> singletons = new ConcurrentHashMap<>(); @@ -25,11 +23,9 @@ public EasyInjectorImpl(Iterable modules) { providerMethod(module, providerMethod); } } - } - /** * @return an instance of type */ @@ -66,7 +62,7 @@ public Provider getProvider(Key key) { * Dynamic Registers AbstractModules */ @Override - public void addDynamicModule(AbstractModule abstractModule){ + public void addDynamicModule(AbstractModule abstractModule) { for (Method providerMethod : providers(abstractModule.getClass())) { providerMethod(abstractModule, providerMethod); } @@ -148,36 +144,23 @@ private Provider[] paramProviders( final Set> chain ) { Provider[] providers = new Provider[parameterTypes.length]; - CompletableFuture[] futureProviders = new CompletableFuture[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; ++i) { - int finalI = i; - futureProviders[i] = CompletableFuture.supplyAsync(() -> { - Class parameterClass = parameterClasses[finalI]; - Annotation qualifier1 = Key.qualifier(annotations[finalI]); - Class providerType = Provider.class.equals(parameterClass) ? - (Class) ((ParameterizedType) parameterTypes[finalI]).getActualTypeArguments()[0] : - null; - if (providerType == null) { - final Key newKey = Key.of(parameterClass, qualifier1); - final Set> newChain = append(chain, key); - if (newChain.contains(newKey)) { - throw new EasyInjectorRuntimeException(String.format("Circular dependency: %s", chain(newChain, newKey))); - } - return provider(newKey, newChain).get(); - } else { - final Key newKey = Key.of(providerType, qualifier1); - return provider(newKey, null); + Class parameterClass = parameterClasses[i]; + Annotation qualifier = qualifier(annotations[i]); + Class providerType = Provider.class.equals(parameterClass) ? + (Class) ((ParameterizedType) parameterTypes[i]).getActualTypeArguments()[0] : + null; + if (providerType == null) { + final Key newKey = Key.of(parameterClass, qualifier); + final Set> newChain = append(chain, key); + if (newChain.contains(newKey)) { + throw new EasyInjectorRuntimeException(String.format("Circular dependency: %s", chain(newChain, newKey))); } - }); - } - try { - CompletableFuture.allOf(futureProviders).join(); - } catch (CompletionException e) { - throw new EasyInjectorRuntimeException("Error while processing parameters in parallel", e); - } - for (int i = 0; i < parameterTypes.length; ++i) { - int finalI = i; - providers[i] = () -> futureProviders[finalI].join(); + providers[i] = () -> provider(newKey, newChain).get(); + } else { + final Key newKey = Key.of(providerType, qualifier); + providers[i] = () -> provider(newKey, null); + } } return providers; } @@ -299,4 +282,4 @@ private boolean providerInSubClass(Method method, Set discoveredMethods) return false; } -} +} \ No newline at end of file