diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
index c6afd9353a..eb0187f9c4 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
@@ -300,13 +300,15 @@ public abstract class AutowireUtils {
* {@link Qualifier @Qualifier}, or {@link Value @Value}.
*
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 parameter the parameter whose dependency should be autowired (must not be
+ * {@code null})
* @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) {
+ Assert.notNull(parameter, "Parameter must not be null");
AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex);
return (AnnotatedElementUtils.hasAnnotation(annotatedParameter, Autowired.class) ||
AnnotatedElementUtils.hasAnnotation(annotatedParameter, Qualifier.class) ||
@@ -326,14 +328,15 @@ public abstract class AutowireUtils {
* flag set to {@code false}.
*
If an explicit qualifier 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 parameter the parameter whose dependency should be resolved (must not be
+ * {@code null})
* @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
+ * the dependency (must not be {@code null})
* @return the resolved object, or {@code null} if none found
* @throws BeansException if dependency resolution failed
* @see #isAutowirable
@@ -347,6 +350,9 @@ public abstract class AutowireUtils {
Parameter parameter, int parameterIndex, Class> containingClass, AutowireCapableBeanFactory beanFactory)
throws BeansException {
+ Assert.notNull(parameter, "Parameter must not be null");
+ Assert.notNull(beanFactory, "AutowireCapableBeanFactory must not be null");
+
AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex);
Autowired autowired = AnnotatedElementUtils.findMergedAnnotation(annotatedParameter, Autowired.class);
boolean required = (autowired == null || autowired.required());
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/AutowireUtilsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/AutowireUtilsTests.java
index 08aec5ef0e..7ba1b10c6f 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/support/AutowireUtilsTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/AutowireUtilsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,33 +16,40 @@
package org.springframework.beans.factory.support;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.when;
-
import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
+import org.junit.Rule;
import org.junit.Test;
-import org.mockito.Mockito;
+import org.junit.rules.ExpectedException;
+
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.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
/**
+ * Unit tests for {@link AutowireUtils}.
+ *
* @author Juergen Hoeller
* @author Sam Brannen
+ * @author Loïc Ledoyen
*/
public class AutowireUtilsTests {
+
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
@Test
public void genericMethodReturnTypes() {
@@ -97,46 +104,96 @@ public class AutowireUtilsTests {
}
@Test
- public void marked_parameters_are_candidate_for_autowiring() throws NoSuchMethodException {
- Constructor autowirableConstructor = ReflectionUtils.accessibleConstructor(
- AutowirableClass.class, String.class, String.class, String.class, String.class);
+ public void isAutowirablePreconditions() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("Parameter must not be null");
+ AutowireUtils.isAutowirable(null, 0);
+ }
+
+ @Test
+ public void annotatedParametersInMethodAreCandidatesForAutowiring() throws Exception {
+ Method method = getClass().getDeclaredMethod("autowirableMethod", String.class, String.class, String.class, String.class);
+ assertAutowirableParameters(method);
+ }
+
+ @Test
+ public void annotatedParametersInTopLevelClassConstructorAreCandidatesForAutowiring() throws Exception {
+ Constructor> constructor = AutowirableClass.class.getConstructor(String.class, String.class, String.class, String.class);
+ assertAutowirableParameters(constructor);
+ }
+
+ @Test
+ public void annotatedParametersInInnerClassConstructorAreCandidatesForAutowiring() throws Exception {
+ Class> innerClass = AutowirableClass.InnerAutowirableClass.class;
+ assertTrue(ClassUtils.isInnerClass(innerClass));
+ Constructor> constructor = innerClass.getConstructor(AutowirableClass.class, String.class, String.class);
+ assertAutowirableParameters(constructor);
+ }
- for (int parameterIndex = 0; parameterIndex < autowirableConstructor.getParameterCount(); parameterIndex++) {
- Parameter parameter = autowirableConstructor.getParameters()[parameterIndex];
+ private void assertAutowirableParameters(Executable executable) {
+ int startIndex = (executable instanceof Constructor)
+ && ClassUtils.isInnerClass(executable.getDeclaringClass()) ? 1 : 0;
+ Parameter[] parameters = executable.getParameters();
+ for (int parameterIndex = startIndex; parameterIndex < parameters.length; parameterIndex++) {
+ Parameter parameter = parameters[parameterIndex];
assertTrue("Parameter " + parameter + " must be autowirable", AutowireUtils.isAutowirable(parameter, parameterIndex));
}
}
@Test
- public void not_marked_parameters_are_not_candidate_for_autowiring() throws NoSuchMethodException {
- Constructor notAutowirableConstructor = ReflectionUtils.accessibleConstructor(AutowirableClass.class, String.class);
+ public void nonAnnotatedParametersInTopLevelClassConstructorAreNotCandidatesForAutowiring() throws Exception {
+ Constructor> notAutowirableConstructor = AutowirableClass.class.getConstructor(String.class);
- for (int parameterIndex = 0; parameterIndex < notAutowirableConstructor.getParameterCount(); parameterIndex++) {
- Parameter parameter = notAutowirableConstructor.getParameters()[parameterIndex];
- assertFalse("Parameter " + parameter + " must not be autowirable", AutowireUtils.isAutowirable(parameter, 0));
+ Parameter[] parameters = notAutowirableConstructor.getParameters();
+ for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
+ Parameter parameter = parameters[parameterIndex];
+ assertFalse("Parameter " + parameter + " must not be autowirable", AutowireUtils.isAutowirable(parameter, parameterIndex));
}
}
@Test
- public void dependency_resolution_for_marked_parameters() throws NoSuchMethodException {
- Constructor autowirableConstructor = ReflectionUtils.accessibleConstructor(
- AutowirableClass.class, String.class, String.class, String.class, String.class);
- AutowireCapableBeanFactory beanFactory = Mockito.mock(AutowireCapableBeanFactory.class);
- // BeanFactory will return the DependencyDescriptor for convenience and to avoid using an ArgumentCaptor
- when(beanFactory.resolveDependency(any(), isNull())).thenAnswer(iom -> iom.getArgument(0));
-
- for (int parameterIndex = 0; parameterIndex < autowirableConstructor.getParameterCount(); parameterIndex++) {
- Parameter parameter = autowirableConstructor.getParameters()[parameterIndex];
+ public void resolveDependencyPreconditionsForParameter() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("Parameter must not be null");
+ AutowireUtils.resolveDependency(null, 0, null, mock(AutowireCapableBeanFactory.class));
+ }
+
+ @Test
+ public void resolveDependencyPreconditionsForBeanFactory() throws Exception {
+ Method method = getClass().getDeclaredMethod("autowirableMethod", String.class, String.class, String.class, String.class);
+ Parameter parameter = method.getParameters()[0];
+
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("AutowireCapableBeanFactory must not be null");
+ AutowireUtils.resolveDependency(parameter, 0, null, null);
+ }
+
+ @Test
+ public void resolveDependencyForAnnotatedParametersInTopLevelClassConstructor() throws Exception {
+ Constructor> constructor = AutowirableClass.class.getConstructor(String.class, String.class, String.class, String.class);
+
+ AutowireCapableBeanFactory beanFactory = mock(AutowireCapableBeanFactory.class);
+ // Configure the mocked BeanFactory to return the DependencyDescriptor for convenience and
+ // to avoid using an ArgumentCaptor.
+ when(beanFactory.resolveDependency(any(), isNull())).thenAnswer(invocation -> invocation.getArgument(0));
+
+ Parameter[] parameters = constructor.getParameters();
+ for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
+ Parameter parameter = parameters[parameterIndex];
DependencyDescriptor intermediateDependencyDescriptor = (DependencyDescriptor) AutowireUtils.resolveDependency(
parameter, parameterIndex, AutowirableClass.class, beanFactory);
- assertEquals(intermediateDependencyDescriptor.getAnnotatedElement(), autowirableConstructor);
- assertEquals(intermediateDependencyDescriptor.getMethodParameter().getParameter(), parameter);
+ assertEquals(constructor, intermediateDependencyDescriptor.getAnnotatedElement());
+ assertEquals(parameter, intermediateDependencyDescriptor.getMethodParameter().getParameter());
}
}
+
public interface MyInterfaceType {
}
+ public class MySimpleInterfaceType implements MyInterfaceType {
+ }
+
public static class MyTypeWithMethods {
/**
@@ -229,7 +286,15 @@ public class AutowireUtilsTests {
}
}
+ void autowirableMethod(
+ @Autowired String firstParameter,
+ @Qualifier("someQualifier") String secondParameter,
+ @Value("${someValue}") String thirdParameter,
+ @Autowired(required = false) String fourthParameter) {
+ }
+
public static class AutowirableClass {
+
public AutowirableClass(@Autowired String firstParameter,
@Qualifier("someQualifier") String secondParameter,
@Value("${someValue}") String thirdParameter,
@@ -238,8 +303,13 @@ public class AutowireUtilsTests {
public AutowirableClass(String notAutowirableParameter) {
}
- }
- public class MySimpleInterfaceType implements MyInterfaceType {
+ public class InnerAutowirableClass {
+
+ public InnerAutowirableClass(@Autowired String firstParameter,
+ @Qualifier("someQualifier") String secondParameter) {
+ }
+ }
}
+
}