
net.cassite.pure.ioc.IOCController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pure.ioc Show documentation
Show all versions of pure.ioc Show documentation
Lightweight type and annotation based dependency injection framework
The newest version!
package net.cassite.pure.ioc;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import net.cassite.pure.ioc.annotations.Singleton;
import net.cassite.pure.ioc.handlers.*;
import net.cassite.pure.ioc.handlers.constructor.ConstructorDefaultFilter;
import net.cassite.pure.ioc.handlers.constructor.DefaultConstructorFilter;
import net.cassite.pure.ioc.handlers.param.*;
import net.cassite.pure.ioc.handlers.type.*;
import static net.cassite.style.Style.*;
import static net.cassite.style.aggregation.Aggregation.*;
import static net.cassite.style.reflect.Reflect.*;
import net.cassite.style.reflect.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The class is to control the process of handling annotations and constructing
* objects.
* Annotation handlers are divided into 4 kinds.
*
* - TypeAnnotationHandler : enabled when constructing objects
* - ConstructorFilter : enabled when selecting constructors
* - ParamAnnotationHandler : enabled when getting parameter values of a
* constructor
* - SetterAnnotationHandler : enabled when invoking a setter.
* (field, method, parameter annotation of a setter are considered as 'setter
* annotation'.)
*
* All handlers process in a simple logic:
* enable handlers in a chain, low priority handler are earlier to be called,
*
* handlers are recommended to call higher priority handlers, check return
* value, then decide whether to run its own handling process.
*
* @author wkgcass
* @see net.cassite.pure.ioc.handlers.TypeHandlerChain
* @see net.cassite.pure.ioc.handlers.TypeAnnotationHandler
* @see net.cassite.pure.ioc.handlers.type.TypeDefaultHandler
* @see net.cassite.pure.ioc.handlers.ConstructorFilterChain
* @see net.cassite.pure.ioc.handlers.ConstructorFilter
* @see net.cassite.pure.ioc.handlers.ParamHandlerChain
* @see net.cassite.pure.ioc.handlers.ParamAnnotationHandler
*/
public abstract class IOCController {
private static final Logger LOGGER = LoggerFactory.getLogger(IOCController.class);
// ===========================
// ==========handlers=========
// ===========================
/**
* param annotation handlers
*/
private static List paramAnnotationHandlers = new ArrayList<>();
/**
* constructor filters
*/
private static List constructorFilters = new ArrayList<>();
/**
* type annotation handler
*/
private static List typeAnnotationHandlers = new ArrayList<>();
// ============================
// =========container==========
// ============================
/**
* singleton class instances
* classes with Singleton annotation
*
* @see Singleton
*/
private static Map, Object> singletons = new ConcurrentHashMap<>();
public static final Scope rootScope = new Scope();
static {
rootScope.bind(Scope.class, scope -> scope);
}
protected IOCController() {
}
/**
* get instance by class.
* this method would check TypeHandlerChain to generate instance.
*
* @param scope wire scope
* @param cls class
* @param expectingClass the class originally wanted
* @return instance handled by type chain
*/
protected static Object get(Scope scope, Class> cls, Class> expectingClass) {
LOGGER.debug("Invoking get(Class) to get instance of {}", cls);
TypeHandlerChain chain = new TypeHandlerChain(typeAnnotationHandlers, cls.getAnnotations());
return chain.next().handle(scope, cls, expectingClass, chain);
}
@SuppressWarnings("unchecked")
protected static void fillField(Scope scope, Object target, @SuppressWarnings("rawtypes") FieldSupport f) {
Annotation[] annotations = f.getMember().getAnnotations();
LOGGER.debug("Wiring object {}'s field {} with annotations {}", target, f, annotations);
ParamHandlerChain chain = new ParamHandlerChain(paramAnnotationHandlers, annotations);
Object obj = chain.next().handle(scope, f, f.getMember().getType(), f.getMember().getType(), annotations, chain);
f.set(target, obj);
}
/**
* invoke a setter
* This method would check corresponding field(might not found, that's
* unimportant) and method annotations, then go through
* SetterHandlerChain.
*
* @param scope scope
* @param target object to invoke
* @param m setter
*/
@SuppressWarnings("unchecked")
protected static void invokeSetter(Scope scope, Object target, @SuppressWarnings("rawtypes") MethodSupport m) {
LOGGER.debug("Wiring object {}'s method {}", target, m);
List> fields = cls(target).allFields();
Set annset = new HashSet<>();
// get inferred field name
String fieldName = m.name().substring(3);
// try to get field and its annotations ( ignore field name case)
If($(fields).findOne(f -> f.name().equalsIgnoreCase(fieldName)), found -> {
Collections.addAll(annset, found.getMember().getAnnotations());
}).End();
// try to get method annotations
Collections.addAll(annset, m.getMember().getAnnotations());
// parameter value annotations
Collections.addAll(annset, m.getMember().getParameterAnnotations()[0]);
LOGGER.debug("With Annotations: {}", annset);
// handle
Annotation[] annoArray = annset.toArray(new Annotation[annset.size()]);
ParamHandlerChain chain = new ParamHandlerChain(paramAnnotationHandlers, annoArray);
try {
Object o = chain.next().handle(scope, m, m.argTypes()[0], m.argTypes()[0], annoArray, chain);
m.invoke(target, o);
} catch (IrrelevantAnnotationHandlingException | IgnoredAnnotationHandlingException ignore) {
}
}
/**
* construct with given class and parameter values.
*
* @param con constructor to call
* @param parameterValues parameter values
* @return new instance generated by the constructor and parameterValues
*/
private static Object construct(ConstructorSup> con, Object[] parameterValues) {
return con.newInstance(parameterValues);
}
/**
* construct with given class
* this method gets constructor from constructor filter chain, and then
* get parameters from ParamHandlerChain.
* finally call {@link #construct(ConstructorSup, Object[])}
*
* @param scope wire scope
* @param cls class to construct
* @return instance of the class
* @see ParamHandlerChain
*/
protected static Object constructObject(Scope scope, @SuppressWarnings("rawtypes") Class cls) {
LOGGER.debug("Invoking constructObject(Class) to get instance of type {}", cls);
Set set = new HashSet<>();
for (Constructor> cons : cls.getConstructors()) {
Collections.addAll(set, cons.getAnnotations());
}
LOGGER.debug("--gathered annotations are {}", set);
ConstructorFilterChain chain = new ConstructorFilterChain(constructorFilters, set);
@SuppressWarnings("unchecked")
ConstructorSup> con = chain.next().handle(scope, cls(cls).constructors(), chain);
LOGGER.debug("--retrieved constructor is {}", con);
Object[] pv = new Object[con.argCount()];
for (int i = 0; i < pv.length; ++i) {
ParamHandlerChain chain2 = new ParamHandlerChain(paramAnnotationHandlers, con.getMember().getParameterAnnotations()[i]);
pv[i] = chain2.next().handle(scope, con, con.argTypes()[i], con.argTypes()[i], con.getMember().getParameterAnnotations()[i], chain2);
LOGGER.debug("--parameter at index {} is {}", i, pv[i]);
}
return construct(con, pv);
}
/**
* get instance with class.
* this method would check whether it IsSingleton
* then invoke constructObject(Class<?>) to get instance
*
* This method might call singletons.get directly if the singleton class
* already in or finished construction or
* {@link #constructObject(Scope, Class)}
* Note that, this method will construct the given class, it will
* not go through TypeHandlerChain, e.g. it won't find out whether it's
* redirected to another class(using Default annotation)
* Use {@link #get(Scope, Class, Class)} to do the type check.
*
* @param scope wire session
* @param cls class to instantiate
* @return instance of the class.
* @see #constructObject(Scope, Class)
* @see #get(Scope, Class, Class)
*/
@SuppressWarnings("unchecked")
protected static Object getObject(Scope scope, @SuppressWarnings("rawtypes") Class cls) {
LOGGER.debug("Invoking getObject(Class) to get instance of type {}", cls);
if (cls.isAnnotationPresent(Singleton.class)) {
LOGGER.debug("--is singleton");
if (singletons.containsKey(cls)) {
return singletons.get(cls);
} else {
return constructObject(scope, cls);
}
} else {
return constructObject(scope, cls);
}
}
/**
* This method invokes given method with inferred arguments
* It will check the ParamHandlerChain, and retrieve arguments.
*
* @param scope wire scope
* @param method method to invoke
* @param target object of the method to invoke on
* @return invocation result
* @see ParamHandlerChain
*/
@SuppressWarnings({"unchecked"})
protected static Object invokeMethod(Scope scope, @SuppressWarnings("rawtypes") MethodSupport method, Object target) {
LOGGER.debug("Invoking method {} of object {}", method, target);
Object[] pv = new Object[method.argCount()];
for (int i = 0; i < pv.length; ++i) {
ParamHandlerChain chain2 = new ParamHandlerChain(paramAnnotationHandlers, method.getMember().getParameterAnnotations()[i]);
pv[i] = chain2.next().handle(scope, method, method.argTypes()[i], method.argTypes()[i], method.getMember().getParameterAnnotations()[i], chain2);
}
return method.invoke(target, pv);
}
// ================================
// ========register/retrieve=======
// ================================
public static void register(ConstructorFilter ah) {
LOGGER.info("registering {}", ah);
constructorFilters.add(ah);
}
public static void register(ParamAnnotationHandler ah) {
LOGGER.info("registering {}", ah);
paramAnnotationHandlers.add(ah);
}
public static void register(TypeAnnotationHandler ah) {
LOGGER.info("registering {}", ah);
typeAnnotationHandlers.add(ah);
}
static void registerSingleton(Object instance) {
if (singletons.containsKey(instance.getClass())) {
throw new ConstructingMultiSingletonException(instance.getClass());
}
if (null != instance.getClass().getAnnotation(Singleton.class)) {
singletons.put(instance.getClass(), instance);
}
}
/**
* Automatically register built-in handlers
*/
public static void autoRegister() {
LOGGER.info("start auto registering...");
register(new DefaultConstructorFilter());
register(new ConstructorDefaultFilter());
register(new ParamScopeHandler());
register(new PrimitiveParameterHandler());
register(new DefaultParamHandler());
register(new ParamUseHandler());
register(new ParamExtendHandler());
register(new ParamForceHandler());
register(new ParamIgnoreHandler());
register(new TypeAOPHandler());
register(new TypeWireHandler());
register(new DefaultTypeHandler());
register(new TypeIsSingletonHandler());
register(new TypeDefaultHandler());
register(new TypeExtendHandler());
}
public static void closeRegistering() {
paramAnnotationHandlers = readOnly(paramAnnotationHandlers);
constructorFilters = readOnly(constructorFilters);
typeAnnotationHandlers = readOnly(typeAnnotationHandlers);
LOGGER.info("registration closed.");
}
/**
* determine whether there are handlers registered
*
* @return true if handlers had been registered, false otherwise
*/
static boolean isWithHandlers() {
return !typeAnnotationHandlers.isEmpty() || !constructorFilters.isEmpty() || !paramAnnotationHandlers.isEmpty();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy