Skip to content

Open parameter autowiring utility for external use #2060

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

Merged
merged 2 commits into from
Feb 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
Expand All @@ -33,8 +36,17 @@
import java.util.Set;

import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
Expand All @@ -49,13 +61,29 @@
* @since 1.1.2
* @see AbstractAutowireCapableBeanFactory
*/
abstract class AutowireUtils {
public abstract class AutowireUtils {

private static final Comparator<Executable> EXECUTABLE_COMPARATOR = (e1, e2) -> {
int result = Boolean.compare(Modifier.isPublic(e2.getModifiers()), Modifier.isPublic(e1.getModifiers()));
return result != 0 ? result : Integer.compare(e2.getParameterCount(), e1.getParameterCount());
};

private static final AnnotatedElement EMPTY_ANNOTATED_ELEMENT = new AnnotatedElement() {
@Override
@Nullable
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return null;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
@Override
public Annotation[] getDeclaredAnnotations() {
return new Annotation[0];
}
};


/**
* Sort the given constructors, preferring public constructors and "greedy" ones with
Expand All @@ -64,7 +92,7 @@ abstract class AutowireUtils {
* decreasing number of arguments.
* @param constructors the constructor array to sort
*/
public static void sortConstructors(Constructor<?>[] constructors) {
static void sortConstructors(Constructor<?>[] constructors) {
Arrays.sort(constructors, EXECUTABLE_COMPARATOR);
}

Expand All @@ -75,7 +103,7 @@ public static void sortConstructors(Constructor<?>[] constructors) {
* decreasing number of arguments.
* @param factoryMethods the factory method array to sort
*/
public static void sortFactoryMethods(Method[] factoryMethods) {
static void sortFactoryMethods(Method[] factoryMethods) {
Arrays.sort(factoryMethods, EXECUTABLE_COMPARATOR);
}

Expand All @@ -85,7 +113,7 @@ public static void sortFactoryMethods(Method[] factoryMethods) {
* @param pd the PropertyDescriptor of the bean property
* @return whether the bean property is excluded
*/
public static boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
static boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
Method wm = pd.getWriteMethod();
if (wm == null) {
return false;
Expand All @@ -107,7 +135,7 @@ public static boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
* @param interfaces the Set of interfaces (Class objects)
* @return whether the setter method is defined by an interface
*/
public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> interfaces) {
static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> interfaces) {
Method setter = pd.getWriteMethod();
if (setter != null) {
Class<?> targetClass = setter.getDeclaringClass();
Expand All @@ -128,7 +156,7 @@ public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Clas
* @param requiredType the type to assign the result to
* @return the resolved value
*/
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
Expand Down Expand Up @@ -173,7 +201,7 @@ public static Object resolveAutowiringValue(Object autowiringValue, Class<?> req
* @return the resolved target return type or the standard method return type
* @since 3.2.5
*/
public static Class<?> resolveReturnTypeForFactoryMethod(
static Class<?> resolveReturnTypeForFactoryMethod(
Method method, Object[] args, @Nullable ClassLoader classLoader) {

Assert.notNull(method, "Method must not be null");
Expand Down Expand Up @@ -264,6 +292,103 @@ else if (arg instanceof TypedStringValue) {
return method.getReturnType();
}

/**
* Determine if the supplied {@link Parameter} can <em>potentially</em> be
* autowired from an {@link AutowireCapableBeanFactory}.
* <p>Returns {@code true} if the supplied parameter is annotated or
* meta-annotated with {@link Autowired @Autowired},
* {@link Qualifier @Qualifier}, or {@link Value @Value}.
* <p>Note that {@link #resolveDependency} may still be able to resolve the
* dependency for the supplied parameter even if this method returns {@code false}.
* @param parameter the parameter whose dependency should be autowired
* @param parameterIndex the index of the parameter in the constructor or method
* that declares the parameter
* @see #resolveDependency
* @since 5.2
*/
public static boolean isAutowirable(Parameter parameter, int parameterIndex) {
AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex);
return (AnnotatedElementUtils.hasAnnotation(annotatedParameter, Autowired.class) ||
AnnotatedElementUtils.hasAnnotation(annotatedParameter, Qualifier.class) ||
AnnotatedElementUtils.hasAnnotation(annotatedParameter, Value.class));
}

/**
* Resolve the dependency for the supplied {@link Parameter} from the
* supplied {@link AutowireCapableBeanFactory}.
* <p>Provides comprehensive autowiring support for individual method parameters
* on par with Spring's dependency injection facilities for autowired fields and
* methods, including support for {@link Autowired @Autowired},
* {@link Qualifier @Qualifier}, and {@link Value @Value} with support for property
* placeholders and SpEL expressions in {@code @Value} declarations.
* <p>The dependency is required unless the parameter is annotated or meta-annotated
* with {@link Autowired @Autowired} with the {@link Autowired#required required}
* flag set to {@code false}.
* <p>If an explicit <em>qualifier</em> is not declared, the name of the parameter
* will be used as the qualifier for resolving ambiguities.
* @param parameter the parameter whose dependency should be resolved
* @param parameterIndex the index of the parameter in the constructor or method
* that declares the parameter
* @param containingClass the concrete class that contains the parameter; this may
* differ from the class that declares the parameter in that it may be a subclass
* thereof, potentially substituting type variables
* @param beanFactory the {@code AutowireCapableBeanFactory} from which to resolve
* the dependency
* @return the resolved object, or {@code null} if none found
* @throws BeansException if dependency resolution failed
* @see #isAutowirable
* @see Autowired#required
* @see SynthesizingMethodParameter#forExecutable(Executable, int)
* @see AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor, String)
* @since 5.2
*/
@Nullable
public static Object resolveDependency(
Parameter parameter, int parameterIndex, Class<?> containingClass, AutowireCapableBeanFactory beanFactory)
throws BeansException {

AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex);
Autowired autowired = AnnotatedElementUtils.findMergedAnnotation(annotatedParameter, Autowired.class);
boolean required = (autowired == null || autowired.required());

MethodParameter methodParameter = SynthesizingMethodParameter.forExecutable(
parameter.getDeclaringExecutable(), parameterIndex);
DependencyDescriptor descriptor = new DependencyDescriptor(methodParameter, required);
descriptor.setContainingClass(containingClass);
return beanFactory.resolveDependency(descriptor, null);
}

/**
* Due to a bug in {@code javac} on JDK versions prior to JDK 9, looking up
* annotations directly on a {@link Parameter} will fail for inner class
* constructors.
* <h4>Bug in javac in JDK &lt; 9</h4>
* <p>The parameter annotations array in the compiled byte code excludes an entry
* for the implicit <em>enclosing instance</em> parameter for an inner class
* constructor.
* <h4>Workaround</h4>
* <p>This method provides a workaround for this off-by-one error by allowing the
* caller to access annotations on the preceding {@link Parameter} object (i.e.,
* {@code index - 1}). If the supplied {@code index} is zero, this method returns
* an empty {@code AnnotatedElement}.
* <h4>WARNING</h4>
* <p>The {@code AnnotatedElement} returned by this method should never be cast and
* treated as a {@code Parameter} since the metadata (e.g., {@link Parameter#getName()},
* {@link Parameter#getType()}, etc.) will not match those for the declared parameter
* at the given index in an inner class constructor.
* @return the supplied {@code parameter} or the <em>effective</em> {@code Parameter}
* if the aforementioned bug is in effect
*/
private static AnnotatedElement getEffectiveAnnotatedParameter(Parameter parameter, int index) {
Executable executable = parameter.getDeclaringExecutable();
if (executable instanceof Constructor && ClassUtils.isInnerClass(executable.getDeclaringClass()) &&
executable.getParameterAnnotations().length == executable.getParameterCount() - 1) {
// Bug in javac in JDK <9: annotation array excludes enclosing instance parameter
// for inner classes, so access it with the actual parameter index lowered by 1
return (index == 0 ? EMPTY_ANNOTATED_ELEMENT : executable.getParameters()[index - 1]);
}
return parameter;
}

/**
* Reflective InvocationHandler for lazy access to the current target object.
Expand Down
Loading