Updated Injection Classes. Unit Tests still required.
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
package de.neitzel.fx.component;
|
||||
|
||||
import javafx.beans.property.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AutoViewModel<T> {
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package de.neitzel.fx.injectfx;
|
||||
|
||||
import de.neitzel.core.inject.InjectableComponentScanner;
|
||||
import de.neitzel.core.inject.ComponentData;
|
||||
import de.neitzel.core.inject.ComponentScanner;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Manages the creation and storage of singleton instances for all @FXMLComponent classes.
|
||||
@@ -11,18 +15,19 @@ import java.util.*;
|
||||
*/
|
||||
public class FXMLComponentInstances {
|
||||
|
||||
@Getter
|
||||
/** Map holding instances of all @FXMLComponent classes, indexed by class and its unique superclasses/interfaces. */
|
||||
private final Map<Class<?>, Object> instanceMap = new HashMap<>();
|
||||
|
||||
/** The InjectableComponents instance that provides information about instantiable components. */
|
||||
private final InjectableComponentScanner injectableScanner;
|
||||
private final ComponentScanner injectableScanner;
|
||||
|
||||
/**
|
||||
* Constructs an FXMLComponentInstances manager and initializes all component instances.
|
||||
*
|
||||
* @param injectableScanner The InjectableComponents instance containing resolved component types.
|
||||
*/
|
||||
public FXMLComponentInstances(InjectableComponentScanner injectableScanner) {
|
||||
public FXMLComponentInstances(ComponentScanner injectableScanner) {
|
||||
this.injectableScanner = injectableScanner;
|
||||
createAllInstances();
|
||||
}
|
||||
@@ -47,7 +52,7 @@ public class FXMLComponentInstances {
|
||||
return instanceMap.get(componentClass);
|
||||
}
|
||||
|
||||
Class<?> concreteClass = injectableScanner.getInstantiableComponents().get(componentClass);
|
||||
Class<?> concreteClass = injectableScanner.getInstantiableComponents().get(componentClass).getType();
|
||||
if (concreteClass == null) {
|
||||
throw new IllegalStateException("No concrete implementation found for: " + componentClass.getName());
|
||||
}
|
||||
@@ -122,19 +127,10 @@ public class FXMLComponentInstances {
|
||||
private void registerInstance(Class<?> concreteClass, Object instance) {
|
||||
instanceMap.put(concreteClass, instance);
|
||||
|
||||
for (Map.Entry<Class<?>, Class<?>> entry : injectableScanner.getInstantiableComponents().entrySet()) {
|
||||
if (entry.getValue().equals(concreteClass)) {
|
||||
for (Map.Entry<Class<?>, ComponentData> entry : injectableScanner.getInstantiableComponents().entrySet()) {
|
||||
if (entry.getValue().getType().equals(concreteClass)) {
|
||||
instanceMap.put(entry.getKey(), instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the instance map containing all component instances.
|
||||
*
|
||||
* @return A map of class types to their instances.
|
||||
*/
|
||||
public Map<Class<?>, Object> getInstanceMap() {
|
||||
return instanceMap;
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,52 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The InjectingControllerFactory is responsible for creating controller instances for JavaFX that
|
||||
* support dependency injection. It uses a parameter map to resolve and supply dependencies to
|
||||
* controller constructors dynamically during instantiation.
|
||||
*
|
||||
* This class simplifies the process of injecting dependencies into JavaFX controllers by analyzing
|
||||
* the constructors of the given controller classes at runtime. It selects the constructor that best
|
||||
* matches the available dependencies in the parameter map and creates an instance of the controller.
|
||||
*
|
||||
* It implements the Callback interface to provide compatibility with the JavaFX FXMLLoader, allowing
|
||||
* controllers with dependencies to be injected seamlessly during the FXML loading process.
|
||||
*/
|
||||
public class InjectingControllerFactory implements Callback<Class<?>, Object> {
|
||||
/**
|
||||
* A map that stores class-to-object mappings used for dependency injection
|
||||
* in controller instantiation. This map is utilized to resolve and supply
|
||||
* the required dependencies for constructors during the creation of controller
|
||||
* instances.
|
||||
*
|
||||
* Each key in the map represents a class type, and the corresponding value
|
||||
* is the instance of that type. This allows the {@link InjectingControllerFactory}
|
||||
* to use the stored instances to dynamically match and inject dependencies
|
||||
* into controllers at runtime.
|
||||
*/
|
||||
private final Map<Class<?>, Object> parameterMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Adds a mapping between a class and its corresponding object instance
|
||||
* to the parameter map used for dependency injection.
|
||||
*
|
||||
* @param clazz The class type to be associated with the provided object instance.
|
||||
* @param object The object instance to be injected for the specified class type.
|
||||
*/
|
||||
public void addInjectingData(Class<?> clazz, Object object) {
|
||||
parameterMap.put(clazz, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of a controller class using a constructor that matches the dependencies
|
||||
* defined in the parameter map. The method dynamically analyzes the constructors of the given
|
||||
* class and attempts to instantiate the class by injecting required dependencies.
|
||||
*
|
||||
* @param controllerClass the class of the controller to be instantiated
|
||||
* @return an instance of the specified controller class
|
||||
* @throws RuntimeException if an error occurs while creating the controller instance or if no suitable constructor is found
|
||||
*/
|
||||
@Override
|
||||
public Object call(Class<?> controllerClass) {
|
||||
try {
|
||||
@@ -37,6 +76,17 @@ public class InjectingControllerFactory implements Callback<Class<?>, Object> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given constructor can be instantiated using the provided parameter map.
|
||||
* This method checks if the parameter map contains entries for all parameter types required
|
||||
* by the specified constructor.
|
||||
*
|
||||
* @param constructor The constructor to be evaluated for instantiability.
|
||||
* @param parameterMap A map where keys are parameter types and values are the corresponding
|
||||
* instances available for injection.
|
||||
* @return {@code true} if the constructor can be instantiated with the given parameter map;
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
private boolean canBeInstantiated(Constructor<?> constructor, Map<Class<?>, Object> parameterMap) {
|
||||
return Stream.of(constructor.getParameterTypes()).allMatch(parameterMap::containsKey);
|
||||
}
|
||||
|
||||
@@ -1,42 +1,131 @@
|
||||
package de.neitzel.fx.injectfx;
|
||||
|
||||
import de.neitzel.core.inject.InjectableComponentScanner;
|
||||
import de.neitzel.core.inject.ComponentScanner;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* The InjectingFXMLLoader class provides a custom implementation of JavaFX's FXMLLoader
|
||||
* that supports dependency injection. It facilitates the loading of FXML files while
|
||||
* dynamically injecting dependencies into controllers during the loading process.
|
||||
*
|
||||
* This class utilizes the InjectingControllerFactory to enable seamless integration
|
||||
* of dependency injection with JavaFX controllers. Dependencies can be added to the
|
||||
* controller factory, and the loader will use this factory to instantiate controllers
|
||||
* with the appropriate dependencies.
|
||||
*
|
||||
* Features of this loader include:
|
||||
* - Support for dependency injection into JavaFX controllers by using a custom factory.
|
||||
* - Adding custom dependency mappings at runtime.
|
||||
* - Scanning and initializing injectable components from a specified package.
|
||||
*/
|
||||
@Slf4j
|
||||
public class InjectingFXMLLoader {
|
||||
|
||||
/**
|
||||
* Represents an instance of the {@link InjectingControllerFactory}, which is used for creating
|
||||
* and managing controller instances with dependency injection capabilities in a JavaFX application.
|
||||
*
|
||||
* This factory facilitates the injection of dependencies by dynamically resolving and supplying
|
||||
* required objects during controller instantiation. It plays a critical role in enabling seamless
|
||||
* integration of dependency injection with JavaFX's {@link FXMLLoader}.
|
||||
*
|
||||
* The `controllerFactory` is used internally by the {@link InjectingFXMLLoader} to provide a consistent
|
||||
* and extensible mechanism for controller creation while maintaining loose coupling and enhancing testability.
|
||||
*
|
||||
* Key responsibilities:
|
||||
* - Manages a mapping of classes to their injectable instances required for controller instantiation.
|
||||
* - Dynamically analyzes and invokes appropriate constructors for controllers based on the availability
|
||||
* of dependencies.
|
||||
* - Ensures that controllers are created with required dependencies, preventing manual resolution of injections.
|
||||
*
|
||||
* This variable is initialized in the {@link InjectingFXMLLoader} and can be extended with additional
|
||||
* mappings at runtime using relevant methods.
|
||||
*/
|
||||
private final InjectingControllerFactory controllerFactory;
|
||||
|
||||
/**
|
||||
* Default constructor for the InjectingFXMLLoader class.
|
||||
* This initializes the loader with a new instance of the {@link InjectingControllerFactory},
|
||||
* which is used to provide dependency injection capabilities for JavaFX controllers.
|
||||
*
|
||||
* The {@link InjectingControllerFactory} allows for the registration and dynamic injection
|
||||
* of dependencies into controllers when they are instantiated during the FXML loading process.
|
||||
*/
|
||||
public InjectingFXMLLoader() {
|
||||
controllerFactory = new InjectingControllerFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the InjectingFXMLLoader class with a specified
|
||||
* InjectingControllerFactory for dependency injection. This enables the FXMLLoader
|
||||
* to use the provided controller factory to instantiate controllers with their
|
||||
* required dependencies during the FXML loading process.
|
||||
*
|
||||
* @param controllerFactory The controller factory responsible for creating controller
|
||||
* instances with dependency injection support.
|
||||
*/
|
||||
public InjectingFXMLLoader(InjectingControllerFactory controllerFactory) {
|
||||
this.controllerFactory = controllerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of `InjectingFXMLLoader` and initializes the component scanner
|
||||
* for dependency injection based on components found within a specified package.
|
||||
* The scanned components are analyzed and their instances are prepared for injection
|
||||
* using the internal `InjectingControllerFactory`.
|
||||
*
|
||||
* @param packageName The package name to be scanned for injectable components.
|
||||
* Classes within the specified package will be identified
|
||||
* as potential dependencies and made available for injection
|
||||
* into JavaFX controllers during FXML loading.
|
||||
*/
|
||||
public InjectingFXMLLoader(String packageName) {
|
||||
controllerFactory = new InjectingControllerFactory();
|
||||
InjectableComponentScanner scanner = new InjectableComponentScanner(packageName);
|
||||
ComponentScanner scanner = new ComponentScanner(packageName);
|
||||
FXMLComponentInstances instances = new FXMLComponentInstances(scanner);
|
||||
addInjectingData(instances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all injectable data from the given {@code FXMLComponentInstances} to the controller factory.
|
||||
* Iterates through the classes in the instance map and delegates adding each class-instance pair
|
||||
* to the {@link #addInjectingData(Class, Object)} method.
|
||||
*
|
||||
* @param instances An {@code FXMLComponentInstances} object containing the mapping of classes
|
||||
* to their respective singleton instances. This data represents the components
|
||||
* available for dependency injection.
|
||||
*/
|
||||
private void addInjectingData(FXMLComponentInstances instances) {
|
||||
for (var clazz: instances.getInstanceMap().keySet()) {
|
||||
addInjectingData(clazz, instances.getInstance(clazz));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a specific class-object mapping to the controller factory for dependency injection.
|
||||
* This method allows the association of a given class type with its corresponding instance,
|
||||
* enabling dependency injection into controllers during the FXML loading process.
|
||||
*
|
||||
* @param clazz The class type to be associated with the provided object instance.
|
||||
* @param object The object instance to be injected for the specified class type.
|
||||
*/
|
||||
public void addInjectingData(Class<?> clazz, Object object) {
|
||||
controllerFactory.addInjectingData(clazz, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an FXML resource from the provided URL and injects dependencies into its controller
|
||||
* using the configured {@code InjectingControllerFactory}.
|
||||
*
|
||||
* @param url the URL of the FXML file to be loaded
|
||||
* @param <T> the type of the root element defined in the FXML
|
||||
* @return the root element of the FXML file
|
||||
* @throws IOException if an error occurs during the loading of the FXML file
|
||||
*/
|
||||
public <T> T load(URL url) throws IOException {
|
||||
FXMLLoader loader = new FXMLLoader(url);
|
||||
loader.setControllerFactory(controllerFactory);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.neitzel.fx.mvvm;
|
||||
|
||||
import javafx.fxml.Initializable;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
|
||||
@@ -6,8 +6,11 @@ import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A generic ViewModel for JavaFX using reflection to automatically create properties for model attributes.
|
||||
|
||||
Reference in New Issue
Block a user