Skip to content

Commit 8c23dae

Browse files
committed
Implemented @provide annotation
1 parent a88a436 commit 8c23dae

File tree

8 files changed

+129
-8
lines changed

8 files changed

+129
-8
lines changed

dependencyInjection/src/main/java/cat/michal/catbase/injector/CatBaseInjector.java

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import cat.michal.catbase.injector.exceptions.InjectorException;
55

66
import java.lang.reflect.Constructor;
7+
import java.lang.reflect.InvocationTargetException;
8+
import java.lang.reflect.Method;
79
import java.lang.reflect.Modifier;
810
import java.util.*;
911

@@ -27,6 +29,7 @@ public <T> T createInstance(Class<T> clazz) {
2729
if(clazz.isEnum()) {
2830
throw new InjectorException("Enums are not allowed");
2931
}
32+
3033
if(clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
3134
List<? extends Class<? extends T>> implementations = classes.stream()
3235
.filter(clazz::isAssignableFrom)
@@ -64,6 +67,16 @@ public List<Dependency<?>> getAll() {
6467
return Collections.unmodifiableList(dependencies);
6568
}
6669

70+
71+
@SuppressWarnings("unchecked")
72+
public <T> T provideInstance(Method method, Class<?> containingClass) {
73+
try {
74+
return (T) method.invoke(getInstance(containingClass), Arrays.stream(method.getParameterTypes()).map(this::getInstance).toArray());
75+
} catch (IllegalAccessException | InvocationTargetException e) {
76+
throw new InjectorException("Error while invoking " + method, e);
77+
}
78+
}
79+
6780
@Override
6881
public void registerInjectables() {
6982
classes.stream()
@@ -79,10 +92,21 @@ public void registerInjectables() {
7992
return;
8093
}
8194
this.registerDependency(clazz);
95+
96+
Arrays.stream(clazz.getMethods())
97+
.filter(method -> method.isAnnotationPresent(Provide.class))
98+
.forEach(method -> {
99+
if(method.getReturnType().isAnnotationPresent(Component.class)) {
100+
throw new InjectorException("Provide methods annotated with @Component are not allowed");
101+
}
102+
registerDependency(method.getReturnType(), method, clazz);
103+
});
82104
});
83105

84106
//check for nested dependencies
85-
dependencies.forEach(dependency -> Arrays.stream(dependency.getClazz().getDeclaredFields())
107+
dependencies.stream()
108+
.filter(dependency -> dependency.getProvideMethod() == null)
109+
.forEach(dependency -> Arrays.stream(dependency.getClazz().getDeclaredFields())
86110
.filter(field -> !field.isAnnotationPresent(Exclude.class))
87111
.forEach(field -> {
88112
Class<?> fieldType = field.getType();
@@ -103,11 +127,16 @@ public void registerInjectables() {
103127
List<Dependency<?>> sortedDependencies = new ArrayList<>();
104128
dependencies.forEach(dependency -> initializeDependency(dependency, sortedDependencies));
105129

106-
107-
108130
//all dependencies initialized at this point
109131
sortedDependencies.forEach(dependency -> {
110-
dependency.setInstance(createInstance(dependency.getClazz()));
132+
Object instance;
133+
if(dependency.getProvideMethod() == null || dependency.getContainingClass() == null) {
134+
instance = createInstance(dependency.getClazz());
135+
} else {
136+
instance = provideInstance(dependency.getProvideMethod(), dependency.getContainingClass());
137+
}
138+
139+
dependency.setInstance(instance);
111140
injectField(dependency.getInstance());
112141

113142
Arrays.stream(dependency.getClazz().getMethods())
@@ -229,17 +258,23 @@ private <T> T instantiate(Dependency<T> dependency) {
229258
}
230259
}
231260

232-
private <T> void registerDependency(Class<T> clazz) {
261+
private <T> void registerDependency(Class<T> clazz, Method provideMethod, Class<?> containingClass) {
233262
if(this.containsByClass(clazz)) {
234263
throw new InjectorException("Class " + clazz.getName() + " is already registered");
235264
}
236265
String injectableName = clazz.getAnnotation(Component.class) == null ? clazz.getSimpleName() : clazz.getAnnotation(Component.class).value();
237266
if(containsByName(injectableName)) {
238267
throw new InjectorException("Class with component name " + injectableName + " is already registered");
239268
}
240-
Dependency<T> dependency = new Dependency<>(injectableName, clazz, null);
269+
Dependency<T> dependency = new Dependency<>(injectableName, clazz, null, provideMethod, containingClass);
241270
this.dependencies.add(dependency);
242271
}
272+
273+
274+
private <T> void registerDependency(Class<T> clazz) {
275+
registerDependency(clazz, null, null);
276+
}
277+
243278
private <T> boolean containsByClass(Class<T> clazz) {
244279
return dependencies.stream().anyMatch(dependency -> dependency.getClazz().equals(clazz));
245280
}

dependencyInjection/src/main/java/cat/michal/catbase/injector/Dependency.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cat.michal.catbase.injector;
22

3+
import java.lang.reflect.Method;
34
import java.util.ArrayList;
45
import java.util.List;
56

@@ -18,18 +19,30 @@ public class Dependency<T> {
1819
private final Class<T> clazz;
1920
private final List<Dependency<?>> depends;
2021
private T instance;
22+
private final Method provideMethod;
23+
private final Class<?> containingClass;
2124

22-
public Dependency(String name, Class<T> clazz, T instance) {
25+
public Dependency(String name, Class<T> clazz, T instance, Method provideMethod, Class<?> containingClass) {
2326
this.name = name;
2427
this.clazz = clazz;
2528
this.instance = instance;
29+
this.provideMethod = provideMethod;
2630
this.depends = new ArrayList<>();
31+
this.containingClass = containingClass;
2732
}
2833

2934
public Class<?> getClazz() {
3035
return clazz;
3136
}
3237

38+
public Method getProvideMethod() {
39+
return provideMethod;
40+
}
41+
42+
public Class<?> getContainingClass() {
43+
return containingClass;
44+
}
45+
3346
public <D> void addDependency(Dependency<D> dependency) {
3447
this.depends.add(dependency);
3548
}
@@ -50,4 +63,4 @@ public String getName() {
5063
public void setInstance(Object instance) {
5164
this.instance = (T) instance;
5265
}
53-
}
66+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package cat.michal.catbase.injector;
2+
3+
public class ProvideInjector {
4+
5+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package cat.michal.catbase.injector.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target(ElementType.METHOD)
10+
public @interface Provide {
11+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package cat.michal.catbase.injector;
2+
3+
import cat.michal.catbase.injector.provideMock.WantedDependency;
4+
import org.junit.jupiter.api.Assertions;
5+
import org.junit.jupiter.api.BeforeAll;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.TestInstance;
8+
9+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
10+
public class ProvideTest {
11+
private CatBaseInjector injector;
12+
13+
@BeforeAll
14+
void setup() {
15+
this.injector = new CatBaseInjector("cat.michal.catbase.injector.provideMock");
16+
}
17+
18+
@Test
19+
void testProvide() {
20+
Assertions.assertEquals("Desired Value!", this.injector.getInstance(WantedDependency.class).getField());
21+
}
22+
23+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package cat.michal.catbase.injector.provideMock;
2+
3+
import cat.michal.catbase.injector.annotations.Component;
4+
5+
@Component
6+
public class NeededDependency {
7+
public String field = "Desired";
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package cat.michal.catbase.injector.provideMock;
2+
3+
import cat.michal.catbase.injector.annotations.Component;
4+
import cat.michal.catbase.injector.annotations.Provide;
5+
6+
@Component
7+
public class Provider {
8+
9+
@Provide
10+
public WantedDependency provide(NeededDependency depend) {
11+
return new WantedDependency(depend.field + " Value!");
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package cat.michal.catbase.injector.provideMock;
2+
3+
public class WantedDependency {
4+
private final String field;
5+
6+
public WantedDependency(String field) {
7+
this.field = field;
8+
}
9+
10+
public String getField() {
11+
return field;
12+
}
13+
}

0 commit comments

Comments
 (0)