Refractor Dependency Injector -> Add public methods for developper's usage, optimise searching by using correctly the cache, add toString() method for debugs

This commit is contained in:
jeffcheasey88 2024-09-20 23:38:20 +02:00
parent 2cf4363e25
commit c06cc46787
3 changed files with 34 additions and 29 deletions

View file

@ -1,6 +1,7 @@
package dev.peerat.framework; package dev.peerat.framework;
import java.lang.reflect.Constructor; import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -11,14 +12,13 @@ import java.util.function.BiFunction;
public class DependencyInjector{ public class DependencyInjector{
private Map<String, Object> map; private Map<String, Object> map;
private List<BiFunction<Constructor<?>, Parameter, Object>> builders; private List<BiFunction<Injection, Class<?>, Object>> builders;
private Object[] injections; private Object[] injections;
private Map<Class<?>, Object> cache; private Map<Class<?>, Object> cache;
public DependencyInjector(){ public DependencyInjector(){
this.map = new HashMap<>(); this.map = new HashMap<>();
this.builders = new ArrayList<>(); this.builders = new ArrayList<>();
this.cache = new HashMap<>();
} }
public DependencyInjector of(String name, Object value){ public DependencyInjector of(String name, Object value){
@ -26,7 +26,7 @@ public class DependencyInjector{
return this; return this;
} }
public DependencyInjector of(BiFunction<Constructor<?>, Parameter, Object> builder){ public DependencyInjector of(BiFunction<Injection, Class<?>, Object> builder){
this.builders.add(builder); this.builders.add(builder);
return this; return this;
} }
@ -34,6 +34,7 @@ public class DependencyInjector{
public DependencyInjector of(Object... injections){ public DependencyInjector of(Object... injections){
if(this.injections == null){ if(this.injections == null){
this.injections = injections; this.injections = injections;
this.cache = new HashMap<>();
return this; return this;
} }
Object[] copy = new Object[this.injections.length+injections.length]; Object[] copy = new Object[this.injections.length+injections.length];
@ -43,31 +44,43 @@ public class DependencyInjector{
return this; return this;
} }
Object applyDependency(Constructor<?> constructor, Parameter parameter){ public Object[] apply(Executable executable) throws Exception{
Injection annotation = parameter.getAnnotation(Injection.class); Parameter[] parameters = executable.getParameters();
Object[] injections = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++) injections[i] = find(parameters[i].getAnnotation(Injection.class), parameters[i].getType());
return injections;
}
public Object apply(Field field) throws Exception{
return find(field.getAnnotation(Injection.class), field.getType());
}
public Object find(Injection annotation, Class<?> type){
if(annotation != null){ if(annotation != null){
Object result = this.map.get(annotation.value()); Object result = this.map.get(annotation.value());
if(result == null) throw new IllegalArgumentException("No depdency named "+annotation.value()+" ("+this.map.keySet()+") in constructor "+constructor); if(result == null) throw new IllegalArgumentException("No depdency named "+annotation.value()+" ("+this.map.keySet()+")");
return result; return result;
} }
for(BiFunction<Constructor<?>, Parameter, Object> function : builders){ for(BiFunction<Injection, Class<?>, Object> function : builders){
Object result = function.apply(constructor, parameter); Object result = function.apply(annotation, type);
if(result != null) return result; if(result != null) return result;
} }
Class<?> type = parameter.getType(); if(this.injections == null) throw new IllegalArgumentException("No dependency for type "+type);
if(this.injections == null) throw new IllegalArgumentException("No dependency for type "+type+" in constructor "+constructor); Object result = this.cache.get(type);
Object result = null; if(result != null) return result;
for(Object injection : injections){ for(Object injection : injections){
if(type.isAssignableFrom(injection.getClass())){ if(type.isAssignableFrom(injection.getClass())){
if(cache.containsKey(type)){ if(cache.containsKey(type)) throw new IllegalArgumentException("Double dependency for type "+type);
throw new IllegalArgumentException("Double dependency for type "+type+", can be in constructor "+constructor);
}else{
cache.put(type, injection); cache.put(type, injection);
result = injection; result = injection;
} }
} }
} if(result == null) throw new IllegalArgumentException("No dependency for type "+type);
if(result == null) throw new IllegalArgumentException("No dependency for type "+type+" in constructor "+constructor);
return result; return result;
} }
@Override
public String toString(){
return "([DependencyInjector] map="+map+",buildersCount="+builders.size()+",injections="+injections+",cache="+cache+")";
}
} }

View file

@ -4,7 +4,6 @@ import java.lang.reflect.Method;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern;
import dev.peerat.framework.utils.json.JsonMap; import dev.peerat.framework.utils.json.JsonMap;

View file

@ -119,7 +119,7 @@ public class Router<U extends User>{
public Router<U> registerClass(Class<?> clazz, DependencyInjector injector) throws Exception{ public Router<U> registerClass(Class<?> clazz, DependencyInjector injector) throws Exception{
Constructor<?>[] constructors = clazz.getDeclaredConstructors(); Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if(constructors.length != 1) throw new IllegalArgumentException("Class with multiple constructor. Not supported by this framework for the moment."); if(constructors.length != 1) throw new IllegalArgumentException("Class with multiple constructor. Not supported by this framework for the moment.");
Object instance = injectDependencies(constructors[0], injector); Object instance = constructors[0].newInstance(injector.apply(constructors[0]));
for(Method method : clazz.getDeclaredMethods()){ for(Method method : clazz.getDeclaredMethods()){
Route route = method.getAnnotation(Route.class); Route route = method.getAnnotation(Route.class);
@ -167,13 +167,6 @@ public class Router<U extends User>{
return this; return this;
} }
private Object injectDependencies(Constructor<?> constructor, DependencyInjector injector) throws Exception{
Parameter[] parameters = constructor.getParameters();
Object[] injections = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++) injections[i] = injector.applyDependency(constructor, parameters[i]);
return constructor.newInstance(injections);
}
public Router<U> setDefault(Response response){ public Router<U> setDefault(Response response){
this.noFileFound = response; this.noFileFound = response;
return this; return this;