#432 Injector improvements
- Separate FieldInjection from default fallback for no-Inject public no-args constructor classes - Make CommandInitializer a normal, instantiable service - Add various injections instead of fetching through command service
This commit is contained in:
@@ -132,7 +132,8 @@ public class AuthMeServiceInitializer {
|
||||
* @return the instantiated object
|
||||
*/
|
||||
private <T> T instantiate(Class<T> clazz, Set<Class<?>> traversedClasses) {
|
||||
Injection<T> injection = firstNotNull(ConstructorInjection.provide(clazz), FieldInjection.provide(clazz));
|
||||
Injection<T> injection = firstNotNull(
|
||||
ConstructorInjection.provide(clazz), FieldInjection.provide(clazz), InstantiationFallback.provide(clazz));
|
||||
if (injection == null) {
|
||||
throw new IllegalStateException("Did not find injection method for " + clazz + ". Make sure you have "
|
||||
+ "a constructor with @Inject or fields with @Inject. Fields with @Inject require "
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package fr.xephi.authme.initialization;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to denote the collection of AuthMe commands.
|
||||
*/
|
||||
@Target({ElementType.PARAMETER, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface BaseCommands {
|
||||
}
|
||||
@@ -74,7 +74,7 @@ class FieldInjection<T> implements Injection<T> {
|
||||
*
|
||||
* @param clazz the class to provide field injection for
|
||||
* @param <T> the class' type
|
||||
* @return field injection provider for the given class
|
||||
* @return field injection provider for the given class, or null if not applicable
|
||||
*/
|
||||
public static <T> Provider<FieldInjection<T>> provide(final Class<T> clazz) {
|
||||
return new Provider<FieldInjection<T>>() {
|
||||
@@ -85,7 +85,7 @@ class FieldInjection<T> implements Injection<T> {
|
||||
return null;
|
||||
}
|
||||
List<Field> fields = getInjectionFields(clazz);
|
||||
return fields == null ? null : new FieldInjection<>(constructor, fields);
|
||||
return fields.isEmpty() ? null : new FieldInjection<>(constructor, fields);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package fr.xephi.authme.initialization;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Fallback instantiation method for classes with an accessible no-args constructor
|
||||
* and no no {@link Inject} annotations whatsoever.
|
||||
*/
|
||||
public class InstantiationFallback<T> implements Injection<T> {
|
||||
|
||||
private final Constructor<T> constructor;
|
||||
|
||||
private InstantiationFallback(Constructor<T> constructor) {
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?>[] getDependencies() {
|
||||
return new Class<?>[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?>[] getDependencyAnnotations() {
|
||||
return new Class<?>[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public T instantiateWith(Object... values) {
|
||||
if (values == null || values.length > 0) {
|
||||
throw new UnsupportedOperationException("Instantiation fallback cannot have parameters");
|
||||
}
|
||||
try {
|
||||
return constructor.newInstance();
|
||||
} catch (InvocationTargetException | IllegalAccessException | InstantiationException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instantiation fallback if the class is applicable.
|
||||
*
|
||||
* @param clazz the class
|
||||
* @param <T> the class' type
|
||||
* @return instantiation fallback provider for the given class, or null if not applicable
|
||||
*/
|
||||
public static <T> Provider<InstantiationFallback<T>> provide(final Class<T> clazz) {
|
||||
return new Provider<InstantiationFallback<T>>() {
|
||||
@Override
|
||||
public InstantiationFallback<T> get() {
|
||||
Constructor<T> noArgsConstructor = getNoArgsConstructor(clazz);
|
||||
// Return fallback only if we have no args constructor and no @Inject annotation anywhere
|
||||
if (noArgsConstructor != null
|
||||
&& !isInjectAnnotationPresent(clazz.getDeclaredConstructors())
|
||||
&& !isInjectAnnotationPresent(clazz.getDeclaredFields())
|
||||
&& !isInjectAnnotationPresent(clazz.getDeclaredMethods())) {
|
||||
return new InstantiationFallback<>(noArgsConstructor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static <T> Constructor<T> getNoArgsConstructor(Class<T> clazz) {
|
||||
try {
|
||||
// Note ljacqu 20160504: getConstructor(), unlike getDeclaredConstructor(), only considers public members
|
||||
return clazz.getConstructor();
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static <A extends AccessibleObject> boolean isInjectAnnotationPresent(A[] accessibles) {
|
||||
for (A accessible : accessibles) {
|
||||
if (accessible.isAnnotationPresent(Inject.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user