-
Notifications
You must be signed in to change notification settings - Fork 997
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Jv dependency injection solution #699
base: main
Are you sure you want to change the base?
Changes from 5 commits
23c8395
a73b402
a9680e4
ab51f72
4c63ad8
9ac9ff3
79446d5
a122956
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
package mate.academy.lib; | ||
|
||
public @interface Component { | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target(ElementType.TYPE) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface Component { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
package mate.academy.lib; | ||
|
||
public @interface Inject { | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target(ElementType.FIELD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface Inject { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,139 @@ | ||
package mate.academy.lib; | ||
|
||
import java.io.File; | ||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.Field; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
public class Injector { | ||
private static final Injector injector = new Injector(); | ||
private static final Map<Class<?>, Class<?>> interfacesImplementations; | ||
private static final Map<Class<?>, Object> instances = new HashMap<>(); | ||
private static final String SERVICE_PACKAGE = "mate.academy.service"; | ||
private static final String SERVICE_PACKAGE_IMPL = "mate.academy.service.impl"; | ||
private static final String CLASS_INDICATOR = ".class"; | ||
|
||
static { | ||
interfacesImplementations = interfacesImplementationsConfiguration(); | ||
} | ||
|
||
public static Injector getInjector() { | ||
return injector; | ||
} | ||
|
||
public Object getInstance(Class<?> interfaceClazz) { | ||
return null; | ||
Object clazzImplementationInstance = null; | ||
Class<?> clazz = findImplementation(interfaceClazz); | ||
Field[] fields = clazz.getDeclaredFields(); | ||
for (Field field : fields) { | ||
if (field.isAnnotationPresent(Inject.class)) { | ||
Object fieldInstance = getInstance(field.getType()); | ||
clazzImplementationInstance = createNewInstance(clazz); | ||
field.setAccessible(true); | ||
try { | ||
field.set(clazzImplementationInstance, fieldInstance); | ||
} catch (IllegalAccessException e) { | ||
throw new RuntimeException("Can't initialize field value. Class: " | ||
+ clazz.getName() + ", field: " + field.getName()); | ||
} | ||
} | ||
} | ||
if (clazzImplementationInstance == null) { | ||
clazzImplementationInstance = createNewInstance(clazz); | ||
} | ||
return clazzImplementationInstance; | ||
} | ||
|
||
private static Object createNewInstance(Class<?> clazz) { | ||
if (instances.containsKey(clazz)) { | ||
return instances.get(clazz); | ||
} | ||
try { | ||
Constructor<?> constructor = clazz.getConstructor(); | ||
Object instance = constructor.newInstance(); | ||
instances.put(clazz, instance); | ||
return instance; | ||
} catch (ReflectiveOperationException e) { | ||
throw new RuntimeException("Can't create new instance of class - " + clazz.getName()); | ||
} | ||
} | ||
|
||
private static Class<?> findImplementation(Class<?> interfaceClazz) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why all private methods is static? |
||
if (interfaceClazz.isInterface()) { | ||
if (!interfacesImplementations.containsKey(interfaceClazz)) { | ||
throw new RuntimeException("Can`t find interface " + interfaceClazz.getName()); | ||
} | ||
Class<?> implementation = interfacesImplementations.get(interfaceClazz); | ||
if (implementation == null) { | ||
throw new RuntimeException("Interface " + interfaceClazz.getName() | ||
+ " has no implementations or they doesn't annotated by @Component"); | ||
} | ||
return implementation; | ||
} | ||
componentCheck(interfaceClazz); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should decide how to check exceptions: in separate methods or in this method. |
||
return interfaceClazz; | ||
} | ||
|
||
private static Map<Class<?>, Class<?>> interfacesImplementationsConfiguration() { | ||
Map<Class<?>, Class<?>> interfacesImplementations = new HashMap<>(); | ||
for (Class<?> clazzInterface : findServiceInterfaces()) { | ||
Class<?> interfaceImplementation = null; | ||
for (Class<?> clazzImplementation : findComponents()) { | ||
if (clazzInterface.isAssignableFrom(clazzImplementation)) { | ||
interfaceImplementation = clazzImplementation; | ||
break; | ||
} | ||
} | ||
System.out.println(clazzInterface + " " + interfaceImplementation); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why you are printing results? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. used for quick check, forgot to delete) |
||
interfacesImplementations.put(clazzInterface, interfaceImplementation); | ||
} | ||
return interfacesImplementations; | ||
} | ||
|
||
private static List<Class<?>> findComponents() { | ||
return findClassesByPackage(SERVICE_PACKAGE_IMPL).stream() | ||
.filter(clazz -> clazz.isAnnotationPresent(Component.class)) | ||
.toList(); | ||
} | ||
|
||
private static List<Class<?>> findServiceInterfaces() { | ||
return findClassesByPackage(SERVICE_PACKAGE).stream() | ||
.filter(Class::isInterface) | ||
.toList(); | ||
} | ||
|
||
public static List<Class<?>> findClassesByPackage(String packageName) { | ||
String path = packageName.replace('.', '/'); | ||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); | ||
File packageDirectory = new File(classLoader.getResource(path).getFile()); | ||
File[] files = packageDirectory.listFiles(); | ||
return Arrays.stream(files) | ||
.filter(File::isFile) | ||
.filter(file -> file.getName().endsWith(CLASS_INDICATOR)) | ||
.map(file -> getClass(file.getName(), packageName)) | ||
.collect(Collectors.toList()); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. public methods should be before private |
||
|
||
private static Class<?> getClass(String className, String packageName) { | ||
try { | ||
String clearedClassName = className.substring(0, | ||
className.length() - CLASS_INDICATOR.length()); | ||
return Class.forName(packageName + "." + clearedClassName); | ||
} catch (ClassNotFoundException e) { | ||
throw new RuntimeException("Can't find class " + className); | ||
} | ||
} | ||
|
||
private static void componentCheck(Class<?> clazz) { | ||
if (!clazz.isAnnotationPresent(Component.class)) { | ||
throw new RuntimeException("Unsupported class " + clazz | ||
+ ", should be annotated by @Component"); | ||
} | ||
|
||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my point of view, we need to follow KISS principles and use just "Map.of(...)" for interface implementations.