Updated Injection Classes. Unit Tests still required.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package de.neitzel.core.commandline;
|
||||
|
||||
import lombok.*;
|
||||
import lombok.extern.log4j.Log4j;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.Singular;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class Configuration {
|
||||
*
|
||||
* @param properties the Properties object containing configuration key-value pairs
|
||||
*/
|
||||
public Configuration(Properties properties) {
|
||||
public Configuration(final Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package de.neitzel.core.image;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Utility class for scaling images while maintaining the aspect ratio.
|
||||
* Provides methods to create scaled images either as byte arrays or InputStreams.
|
||||
*/
|
||||
@Slf4j
|
||||
public class ImageScaler {
|
||||
|
||||
/**
|
||||
@@ -148,7 +152,8 @@ public class ImageScaler {
|
||||
ImageIO.write(scaledImage, TARGET_FORMAT, stream);
|
||||
return stream.toByteArray();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
} catch (IOException ex) {
|
||||
log.error("IOException while scaling image.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package de.neitzel.core.inject;
|
||||
|
||||
import de.neitzel.core.inject.annotation.Config;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents the context of the application and serves as the foundation of a dependency
|
||||
* injection framework.
|
||||
*
|
||||
* The {@code ApplicationContext} is responsible for scanning, instantiating, and managing
|
||||
* the lifecycle of components. Components are identified through the specified base package
|
||||
* (or derived from a configuration class) and instantiated based on their dependency
|
||||
* requirements and scope. The framework supports constructor-based dependency injection
|
||||
* for resolving and creating components.
|
||||
*/
|
||||
public class ApplicationContext {
|
||||
private final Map<Class<?>, ComponentData> components;
|
||||
|
||||
/**
|
||||
* Erstellt einen ApplicationContext auf Basis eines expliziten Package-Namens.
|
||||
*
|
||||
* @param basePackage das Basis-Package, das nach Komponenten durchsucht werden soll
|
||||
*/
|
||||
public ApplicationContext(String basePackage) {
|
||||
ComponentScanner scanner = new ComponentScanner(basePackage);
|
||||
this.components = scanner.getInstantiableComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt einen ApplicationContext auf Basis einer Startklasse.
|
||||
* Das zu scannende Package wird aus der @Config Annotation gelesen (basePackage).
|
||||
* Wenn keine Annotation vorhanden ist oder kein basePackage gesetzt wurde, wird
|
||||
* das Package der übergebenen Klasse verwendet.
|
||||
*
|
||||
* @param configClass Klasse mit oder ohne @Config Annotation
|
||||
*/
|
||||
public ApplicationContext(Class<?> configClass) {
|
||||
String basePackage;
|
||||
Config config = configClass.getAnnotation(Config.class);
|
||||
if (config != null && !config.basePackage().isEmpty()) {
|
||||
basePackage = config.basePackage();
|
||||
} else {
|
||||
basePackage = configClass.getPackageName();
|
||||
}
|
||||
ComponentScanner scanner = new ComponentScanner(basePackage);
|
||||
this.components = scanner.getInstantiableComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an instance of the specified component type from the application context.
|
||||
*
|
||||
* This method uses dependency injection to construct the component if it is not already
|
||||
* a singleton instance. The component's type and scope are determined by the framework,
|
||||
* and the appropriate initialization and lifecycle management are performed.
|
||||
*
|
||||
* @param <T> the type of the component to retrieve
|
||||
* @param type the {@code Class} object representing the type of the component
|
||||
* @return an instance of the requested component type
|
||||
* @throws IllegalArgumentException if no component is found for the specified type
|
||||
* @throws IllegalStateException if no suitable constructor is found for the component
|
||||
* @throws RuntimeException if any error occurs during instantiation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getComponent(Class<? extends T> type) {
|
||||
ComponentData data = components.get(type);
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("No component found for type: " + type.getName());
|
||||
}
|
||||
|
||||
Scope scope = data.getScope();
|
||||
Object instance = data.getInstance();
|
||||
|
||||
if (scope == Scope.SINGLETON && instance != null) {
|
||||
return (T) instance;
|
||||
}
|
||||
|
||||
try {
|
||||
Constructor<?>[] constructors = data.getType().getConstructors();
|
||||
for (Constructor<?> constructor : constructors) {
|
||||
if (canBeInstantiated(constructor, components)) {
|
||||
Class<?>[] paramTypes = constructor.getParameterTypes();
|
||||
Object[] parameters = new Object[paramTypes.length];
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
parameters[i] = getComponent(paramTypes[i]);
|
||||
}
|
||||
instance = constructor.newInstance(parameters);
|
||||
if (scope == Scope.SINGLETON) {
|
||||
data.setInstance(instance);
|
||||
}
|
||||
return (T) instance;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Kein passender Konstruktor gefunden für " + type.getName());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Fehler beim Erstellen der Instanz für " + type.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given constructor can be instantiated using the provided parameter map.
|
||||
*
|
||||
* The method evaluates whether every parameter type required by the constructor
|
||||
* is available in the given parameter map. If all parameter types are present,
|
||||
* the constructor is considered instantiable.
|
||||
*
|
||||
* @param constructor the constructor to check for instantiation feasibility
|
||||
* @param parameterMap a map containing available parameter types and their associated component data
|
||||
* @return true if all parameter types required by the constructor are contained in the parameter map, false otherwise
|
||||
*/
|
||||
private boolean canBeInstantiated(Constructor<?> constructor, Map<Class<?>, ComponentData> parameterMap) {
|
||||
return Stream.of(constructor.getParameterTypes()).allMatch(parameterMap::containsKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a singleton instance of a specific type into the application context.
|
||||
*
|
||||
* This method allows manual addition of singleton components to the context by
|
||||
* providing a concrete instance of the required type. If the type is already mapped
|
||||
* to a different instance or type within the context, an {@link IllegalStateException}
|
||||
* is thrown to prevent replacement of an existing singleton.
|
||||
*
|
||||
* @param <T> the type of the component to register
|
||||
* @param type the class type of the component being added
|
||||
* @param instance the singleton instance of the component to register
|
||||
* @throws IllegalStateException if the type is already registered with a different instance
|
||||
*/
|
||||
public <T> void addSingleton(Class<? extends T> type, T instance) {
|
||||
if (components.get(type) == null ||
|
||||
!Objects.equals(components.get(type).getType().getName(), type.getName())) {
|
||||
|
||||
components.put(type, new ComponentData(type, instance));
|
||||
} else {
|
||||
throw new IllegalStateException("Type cannot be replaced: " + type.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package de.neitzel.core.inject;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Represents a component in a dependency injection framework.
|
||||
*
|
||||
* A component is a unit of functionality that is managed by the framework, allowing
|
||||
* for controlled instantiation and lifecycle management. The component is associated
|
||||
* with a specific type and a scope. The scope determines whether the component is
|
||||
* instantiated as a singleton or as a prototype.
|
||||
*/
|
||||
@Getter
|
||||
public class ComponentData {
|
||||
|
||||
/**
|
||||
* Represents the type of the component being managed by the dependency injection framework.
|
||||
*
|
||||
* This variable holds the class object corresponding to the specific type of the component.
|
||||
* It is used to identify and instantiate the component during the dependency injection process.
|
||||
* The type is immutable and is specified when the component is created.
|
||||
*/
|
||||
private final Class<?> type;
|
||||
|
||||
/**
|
||||
* Defines the lifecycle and instantiation rules for the associated component.
|
||||
*
|
||||
* The {@code Scope} determines whether the component is created as a singleton
|
||||
* (a single shared instance) or as a prototype (a new instance for each request).
|
||||
*
|
||||
* This variable is immutable and represents the specific {@code Scope} assigned
|
||||
* to the component, influencing its behavior within the dependency injection framework.
|
||||
*/
|
||||
private final Scope scope;
|
||||
|
||||
/**
|
||||
* Stores the instantiated object associated with the component.
|
||||
*
|
||||
* This field holds the actual instance of the component's type within the dependency
|
||||
* injection framework. For components with a {@code SINGLETON} scope, this field is
|
||||
* set only once and shared across the entire application. For components with a
|
||||
* {@code PROTOTYPE} scope, this field may be null since a new instance is created
|
||||
* upon each request.
|
||||
*/
|
||||
private Object instance;
|
||||
|
||||
/**
|
||||
* Constructs a new Component instance with the specified type and scope.
|
||||
*
|
||||
* @param type the class type of the component
|
||||
* @param scope the scope of the component, which determines its lifecycle
|
||||
*/
|
||||
public ComponentData(Class<?> type, Scope scope) {
|
||||
this.type = type;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ComponentData instance with the specified type and an initial instance.
|
||||
*
|
||||
* This can be used to add Singletons manually without scanning for them.
|
||||
*
|
||||
* @param type the class type of the component
|
||||
* @param instance the initial instance of the component
|
||||
*/
|
||||
public ComponentData(Class<?> type, Object instance) {
|
||||
this.type = type;
|
||||
this.scope = Scope.SINGLETON;
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the instance for this component if it is configured with a {@code SINGLETON} scope.
|
||||
* This method ensures that the instance is only set once for a {@code SINGLETON} component.
|
||||
* If an instance has already been set, it throws an {@link IllegalStateException}.
|
||||
*
|
||||
* @param instance the object instance to associate with this component
|
||||
* @throws IllegalStateException if an instance has already been set for this {@code SINGLETON} component
|
||||
*/
|
||||
public void setInstance(Object instance) {
|
||||
if (scope != Scope.SINGLETON) {
|
||||
return;
|
||||
}
|
||||
if (this.instance != null) {
|
||||
throw new IllegalStateException("Instance already set for singleton: " + type.getName());
|
||||
}
|
||||
this.instance = instance;
|
||||
}
|
||||
}
|
||||
+73
-51
@@ -1,6 +1,7 @@
|
||||
package de.neitzel.core.inject;
|
||||
|
||||
import de.neitzel.core.inject.annotation.Component;
|
||||
import de.neitzel.core.inject.annotation.Inject;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
@@ -13,7 +14,7 @@ import java.util.stream.Collectors;
|
||||
* The resulting analysis identifies unique and shared interfaces/superclasses as well as
|
||||
* potentially instantiable components, collecting relevant errors if instantiation is not feasible.
|
||||
*/
|
||||
public class InjectableComponentScanner {
|
||||
public class ComponentScanner {
|
||||
|
||||
/**
|
||||
* A set that stores classes annotated with {@code @Component}, representing
|
||||
@@ -30,7 +31,7 @@ public class InjectableComponentScanner {
|
||||
* This field is immutable, ensuring thread safety and consistent state
|
||||
* throughout the lifetime of the {@code InjectableComponentScanner}.
|
||||
*/
|
||||
private final Set<Class<?>> fxmlComponents = new HashSet<>();
|
||||
private final Set<Class<?>> components = new HashSet<>();
|
||||
|
||||
/**
|
||||
* A set of component types that are not uniquely associated with a single implementation.
|
||||
@@ -75,7 +76,7 @@ public class InjectableComponentScanner {
|
||||
* The resolution process checks if a component can be instantiated based on its
|
||||
* constructor dependencies being resolvable using known types or other registered components.
|
||||
*/
|
||||
private final Map<Class<?>, Class<?>> instantiableComponents = new HashMap<>();
|
||||
private final Map<Class<?>, ComponentData> instantiableComponents = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A list of error messages encountered during the scanning and analysis
|
||||
@@ -96,7 +97,7 @@ public class InjectableComponentScanner {
|
||||
*
|
||||
* @param basePackage the base package to scan for injectable components
|
||||
*/
|
||||
public InjectableComponentScanner(String basePackage) {
|
||||
public ComponentScanner(String basePackage) {
|
||||
scanForComponents(basePackage);
|
||||
analyzeComponentTypes();
|
||||
resolveInstantiableComponents();
|
||||
@@ -110,7 +111,7 @@ public class InjectableComponentScanner {
|
||||
*/
|
||||
private void scanForComponents(String basePackage) {
|
||||
Reflections reflections = new Reflections(basePackage);
|
||||
fxmlComponents.addAll(reflections.getTypesAnnotatedWith(Component.class));
|
||||
components.addAll(reflections.getTypesAnnotatedWith(Component.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +136,7 @@ public class InjectableComponentScanner {
|
||||
private void analyzeComponentTypes() {
|
||||
Map<Class<?>, List<Class<?>>> superTypesMap = new HashMap<>();
|
||||
|
||||
for (Class<?> component : fxmlComponents) {
|
||||
for (Class<?> component : components) {
|
||||
Set<Class<?>> allSuperTypes = getAllSuperTypes(component);
|
||||
|
||||
for (Class<?> superType : allSuperTypes) {
|
||||
@@ -156,44 +157,48 @@ public class InjectableComponentScanner {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves components from the set of scanned classes that can be instantiated based on their constructors
|
||||
* and existing known types. The method iteratively processes classes to identify those whose dependencies
|
||||
* can be satisfied, marking them as resolved and registering them alongside their supertypes.
|
||||
* Resolves and identifies instantiable components from a set of scanned components.
|
||||
* This process determines which components can be instantiated based on their dependencies
|
||||
* and class relationships, while tracking unresolved types and potential conflicts.
|
||||
*
|
||||
* If progress is made during a pass (i.e., a component is resolved), the process continues until no more
|
||||
* components can be resolved. Any components that remain unresolved are recorded for further inspection.
|
||||
* The resolution process involves:
|
||||
* 1. Iteratively determining which components can be instantiated using the known types
|
||||
* map. A component is resolvable if its dependencies can be satisfied by the current
|
||||
* set of known types.
|
||||
* 2. Registering resolvable components and their superclasses/interfaces into the known
|
||||
* types map for future iterations.
|
||||
* 3. Removing successfully resolved components from the unresolved set.
|
||||
* 4. Repeating the process until no further components can be resolved in a given iteration.
|
||||
*
|
||||
* The resolved components are stored in a map where each type and its supertypes are associated
|
||||
* with the component class itself. This map allows for subsequent lookups when verifying dependency satisfiability.
|
||||
* At the end of the resolution process:
|
||||
* - Resolvable components are added to the `instantiableComponents` map, which maps types
|
||||
* to their corresponding instantiable implementations.
|
||||
* - Unresolved components are identified, and error details are collected to highlight
|
||||
* dependencies or conflicts preventing their instantiation.
|
||||
*
|
||||
* If unresolved components remain after the resolution process, detailed instantiation errors are collected
|
||||
* for debugging or logging purposes.
|
||||
*
|
||||
* This method depends on the following utility methods:
|
||||
* - {@link #canInstantiate(Class, Set)}: Determines if a component can be instantiated based on existing known types.
|
||||
* - {@link #registerComponentWithSuperTypes(Class, Map)}: Registers a component and its supertypes in the known types map.
|
||||
* - {@link #collectInstantiationErrors(Set)}: Collects detailed error messages for unresolved components.
|
||||
* If errors are encountered due to unresolved components, they are logged for further analysis.
|
||||
*/
|
||||
private void resolveInstantiableComponents() {
|
||||
Set<Class<?>> resolved = new HashSet<>();
|
||||
Set<Class<?>> unresolved = new HashSet<>(fxmlComponents);
|
||||
Map<Class<?>, Class<?>> knownTypes = new HashMap<>();
|
||||
Set<Class<?>> unresolved = new HashSet<>(components);
|
||||
Map<Class<?>, ComponentData> knownTypes = new HashMap<>();
|
||||
Set<Class<?>> resolvableNow;
|
||||
|
||||
boolean progress;
|
||||
do {
|
||||
progress = false;
|
||||
Iterator<Class<?>> iterator = unresolved.iterator();
|
||||
resolvableNow = unresolved.stream()
|
||||
.filter(c -> canInstantiate(c, knownTypes.keySet()))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Class<?> component = iterator.next();
|
||||
if (canInstantiate(component, knownTypes.keySet())) {
|
||||
resolved.add(component);
|
||||
registerComponentWithSuperTypes(component, knownTypes);
|
||||
iterator.remove();
|
||||
progress = true;
|
||||
}
|
||||
for (Class<?> clazz : resolvableNow) {
|
||||
Component annotation = clazz.getAnnotation(Component.class);
|
||||
ComponentData componentInfo = new ComponentData(clazz, annotation.scope());
|
||||
|
||||
resolved.add(clazz);
|
||||
registerComponentWithSuperTypes(componentInfo, knownTypes);
|
||||
}
|
||||
} while (progress);
|
||||
|
||||
unresolved.removeAll(resolvableNow);
|
||||
} while (!resolvableNow.isEmpty());
|
||||
|
||||
instantiableComponents.putAll(knownTypes);
|
||||
|
||||
@@ -203,16 +208,17 @@ public class InjectableComponentScanner {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given component class in the provided map. The component is registered along with all of its
|
||||
* accessible superclasses and interfaces, unless those types are identified as non-unique.
|
||||
* Registers a component and its superclasses or interfaces in the provided map of known types.
|
||||
* This method ensures that the component and its inheritance hierarchy are associated with the
|
||||
* component's data unless the supertype is marked as non-unique.
|
||||
*
|
||||
* @param component the component class to register
|
||||
* @param knownTypes the map where the component and its types will be registered
|
||||
* @param component the {@code ComponentData} instance representing the component to be registered
|
||||
* @param knownTypes the map where component types and their data are stored
|
||||
*/
|
||||
private void registerComponentWithSuperTypes(Class<?> component, Map<Class<?>, Class<?>> knownTypes) {
|
||||
knownTypes.put(component, component);
|
||||
private void registerComponentWithSuperTypes(ComponentData component, Map<Class<?>, ComponentData> knownTypes) {
|
||||
knownTypes.put(component.getType(), component);
|
||||
|
||||
for (Class<?> superType : getAllSuperTypes(component)) {
|
||||
for (Class<?> superType : getAllSuperTypes(component.getType())) {
|
||||
if (!notUniqueTypes.contains(superType)) {
|
||||
knownTypes.put(superType, component);
|
||||
}
|
||||
@@ -220,22 +226,38 @@ public class InjectableComponentScanner {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a given class can be instantiated based on its constructors and
|
||||
* the provided known types. A class is considered instantiable if it has a parameterless
|
||||
* constructor or if all the parameter types of its constructors are present in the known types.
|
||||
* Determines whether the specified component class can be instantiated based on the provided set
|
||||
* of known types. A component is considered instantiable if it has at least one constructor where
|
||||
* all parameter types are contained in the known types set or if it has a no-argument constructor.
|
||||
* Additionally, all fields annotated with {@code @Inject} must have their types present in the
|
||||
* known types set.
|
||||
*
|
||||
* @param component the class to check for instantiation eligibility
|
||||
* @param knownTypes the set of types known to be instantiable and available for constructor injection
|
||||
* @return true if the class can be instantiated; false otherwise
|
||||
* @param component the class to check for instantiability
|
||||
* @param knownTypes the set of currently known types that can be used to satisfy dependencies
|
||||
* @return {@code true} if the component can be instantiated; {@code false} otherwise
|
||||
*/
|
||||
private boolean canInstantiate(Class<?> component, Set<Class<?>> knownTypes) {
|
||||
boolean hasValidConstructor = false;
|
||||
|
||||
for (Constructor<?> constructor : component.getConstructors()) {
|
||||
Class<?>[] paramTypes = constructor.getParameterTypes();
|
||||
if (paramTypes.length == 0 || Arrays.stream(paramTypes).allMatch(knownTypes::contains)) {
|
||||
return true;
|
||||
hasValidConstructor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
if (!hasValidConstructor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var field : component.getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(Inject.class) && !knownTypes.contains(field.getType())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,7 +278,7 @@ public class InjectableComponentScanner {
|
||||
Class<?>[] paramTypes = constructor.getParameterTypes();
|
||||
List<Class<?>> problematicTypes = Arrays.stream(paramTypes)
|
||||
.filter(t -> !instantiableComponents.containsKey(t) && !notUniqueTypes.contains(t))
|
||||
.collect(Collectors.toList());
|
||||
.toList();
|
||||
|
||||
if (problematicTypes.isEmpty()) {
|
||||
possibleWithUniqueTypes = true;
|
||||
@@ -317,7 +339,7 @@ public class InjectableComponentScanner {
|
||||
* @return A map where the key is a class type and the value is the corresponding class implementation
|
||||
* that can be instantiated.
|
||||
*/
|
||||
public Map<Class<?>, Class<?>> getInstantiableComponents() {
|
||||
public Map<Class<?>, ComponentData> getInstantiableComponents() {
|
||||
return instantiableComponents;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package de.neitzel.core.inject;
|
||||
|
||||
/**
|
||||
* Represents the scope of a component in a dependency injection framework.
|
||||
* <p>
|
||||
* The scope determines the lifecycle and visibility of a component instance
|
||||
* within the framework. The available scope types are:
|
||||
* <p>
|
||||
* - {@code SINGLETON}: A single instance of the component is created and
|
||||
* shared across the entire application.
|
||||
* - {@code PROTOTYPE}: A new instance of the component is created
|
||||
* each time it is requested or injected.
|
||||
* <p>
|
||||
* This enumeration is typically used in conjunction with the {@code @Component}
|
||||
* annotation to specify the instantiation behavior of a component.
|
||||
*/
|
||||
public enum Scope {
|
||||
/**
|
||||
* Specifies that the component should be instantiated as a singleton within the
|
||||
* dependency injection framework. A single instance of the component is created
|
||||
* and shared across the entire application lifecycle, ensuring efficient reuse
|
||||
* of resources and consistent behavior for the component throughout the application.
|
||||
* <p>
|
||||
* This scope is typically applied to components where maintaining a single shared
|
||||
* instance is necessary or beneficial, such as managing shared state or providing
|
||||
* utility services.
|
||||
*/
|
||||
SINGLETON,
|
||||
/**
|
||||
* Specifies that the component should be instantiated as a prototype within the
|
||||
* dependency injection framework. A new instance of the component is created
|
||||
* each time it is requested or injected, ensuring that no two requests or injections
|
||||
* share the same instance.
|
||||
* <p>
|
||||
* This scope is typically applied to components where maintaining unique instances
|
||||
* per request is necessary, such as request-specific data processing or handling
|
||||
* of transient state. It allows for complete isolation between multiple users
|
||||
* or operations requiring individual resources.
|
||||
*/
|
||||
PROTOTYPE
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.neitzel.core.inject.annotation;
|
||||
|
||||
import de.neitzel.core.inject.Scope;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -24,4 +26,12 @@ import java.lang.annotation.Target;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Component {
|
||||
/**
|
||||
* Defines the scope of a component within the dependency injection framework.
|
||||
* The scope determines whether the component is instantiated as a singleton or
|
||||
* as a prototype.
|
||||
*
|
||||
* @return the scope of the component, defaulting to {@code Scope.SINGLETON}.
|
||||
*/
|
||||
Scope scope() default Scope.SINGLETON;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package de.neitzel.core.inject.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that an annotated class is a configuration class within a dependency
|
||||
* injection framework. Classes annotated with {@code @Config} are used as markers
|
||||
* for defining settings and application-specific configurations required by the
|
||||
* dependency injection mechanism.
|
||||
*
|
||||
* Typically, configuration classes provide metadata required for setting up the
|
||||
* framework, such as specifying the base package to scan for components.
|
||||
*
|
||||
* This annotation must be applied at the type level and is retained at runtime to
|
||||
* facilitate reflection-based processing. It is intended to serve as a declarative
|
||||
* representation of configuration options for the dependency injection container.
|
||||
*
|
||||
* Attributes:
|
||||
* - {@code basePackage}: Specifies the package name where the framework should scan
|
||||
* for classes annotated with dependency injection annotations such as {@code @Component}.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Config {
|
||||
/**
|
||||
* Specifies the base package for component scanning within the dependency
|
||||
* injection framework. Classes within the defined package and its sub-packages
|
||||
* can be scanned and identified as candidates for dependency injection.
|
||||
*
|
||||
* @return the base package name as a string; returns an empty string by default
|
||||
* if no specific package is defined.
|
||||
*/
|
||||
String basePackage() default "";
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.neitzel.core.inject.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a field is a candidate for dependency injection within
|
||||
* a dependency injection framework. Fields annotated with {@code @Inject}
|
||||
* are automatically populated with the required component instance during runtime,
|
||||
* typically by the dependency injection container.
|
||||
*
|
||||
* This annotation must be applied at the field level and is retained at runtime
|
||||
* to enable reflection-based identification and assignment of dependencies.
|
||||
*
|
||||
* The framework's dependency resolution mechanism identifies the appropriate
|
||||
* instance to inject based on the field's type or custom configuration,
|
||||
* ensuring loose coupling and easier testability.
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Inject {
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package de.neitzel.core.sql;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package de.neitzel.core.sql;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package de.neitzel.core.util;
|
||||
|
||||
import lombok.extern.log4j.Log4j;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package de.neitzel.core.util;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.log4j.Log4j;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
@@ -16,7 +16,7 @@ class InjectableComponentScannerTest {
|
||||
*/
|
||||
@Test
|
||||
void testLoadComponents() {
|
||||
InjectableComponentScanner scanner = new InjectableComponentScanner("de.neitzel.core.inject.testcomponents.test1ok");
|
||||
ComponentScanner scanner = new ComponentScanner("de.neitzel.core.inject.testcomponents.test1ok");
|
||||
var instantiableComponents = scanner.getInstantiableComponents();
|
||||
var nonUniqueTypes = scanner.getNotUniqueTypes();
|
||||
|
||||
@@ -37,7 +37,7 @@ class InjectableComponentScannerTest {
|
||||
*/
|
||||
@Test
|
||||
void testComponentsFailWithUnknownParameters() {
|
||||
InjectableComponentScanner scanner = new InjectableComponentScanner("de.neitzel.core.inject.testcomponents.test2fail");
|
||||
ComponentScanner scanner = new ComponentScanner("de.neitzel.core.inject.testcomponents.test2fail");
|
||||
var instantiableComponents = scanner.getInstantiableComponents();
|
||||
var nonUniqueTypes = scanner.getNotUniqueTypes();
|
||||
|
||||
|
||||
+1
-1
@@ -1,9 +1,9 @@
|
||||
package de.neitzel.core.inject.testcomponents.test1ok.sub;
|
||||
|
||||
import de.neitzel.core.inject.annotation.Component;
|
||||
import de.neitzel.core.inject.testcomponents.test1ok.SuperClass;
|
||||
import de.neitzel.core.inject.testcomponents.test1ok.TestInterface1_1;
|
||||
import de.neitzel.core.inject.testcomponents.test1ok.TestInterface1_2;
|
||||
import de.neitzel.core.inject.annotation.Component;
|
||||
|
||||
@Component
|
||||
public class TestComponent1_2 extends SuperClass implements TestInterface1_1, TestInterface1_2 {
|
||||
|
||||
Reference in New Issue
Block a user