Automatically autowire a bean with one constructor
Previously, if a managed bean had only one non-default constructor, we should still annotate it with `@Autowired` to properly use constructor injection. Not doing so resulted in an error as the container was trying to call the default (non-existing) constructor. This commit updates this behaviour to automatically applyed the autowiring semantic to any bean that has only one constructor. As before, if more than one constructor is defined, `@Autowired` must be specified to teach the container the constructor it has to use. Issue: SPR-12278
This commit is contained in:
parent
3d87718fc6
commit
9e7c791a0f
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
@ -107,6 +107,7 @@ import org.springframework.util.StringUtils;
|
|||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
* @see #setAutowiredAnnotationType
|
||||
* @see Autowired
|
||||
|
@ -312,6 +313,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
|||
}
|
||||
candidateConstructors = candidates.toArray(new Constructor<?>[candidates.size()]);
|
||||
}
|
||||
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterTypes().length > 0) {
|
||||
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
|
||||
}
|
||||
else {
|
||||
candidateConstructors = new Constructor<?>[0];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2002-2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class Spr12278Tests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentSingleConstructor() {
|
||||
this.context = new AnnotationConfigApplicationContext(BaseConfiguration.class,
|
||||
SingleConstructorComponent.class);
|
||||
assertThat(this.context.getBean(SingleConstructorComponent.class).autowiredName, is("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentTwoConstructorsNoHint() {
|
||||
this.context = new AnnotationConfigApplicationContext(BaseConfiguration.class,
|
||||
TwoConstructorsComponent.class);
|
||||
assertThat(this.context.getBean(TwoConstructorsComponent.class).name, is("fallback"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentTwoSpecificConstructorsNoHint() {
|
||||
thrown.expect(BeanCreationException.class);
|
||||
thrown.expectMessage(NoSuchMethodException.class.getName());
|
||||
new AnnotationConfigApplicationContext(BaseConfiguration.class,
|
||||
TwoSpecificConstructorsComponent.class);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
public String autowiredName() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
private static class SingleConstructorComponent {
|
||||
|
||||
private final String autowiredName;
|
||||
|
||||
// No @Autowired - implicit wiring
|
||||
public SingleConstructorComponent(String autowiredName) {
|
||||
this.autowiredName = autowiredName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TwoConstructorsComponent {
|
||||
|
||||
private final String name;
|
||||
|
||||
public TwoConstructorsComponent(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public TwoConstructorsComponent() {
|
||||
this("fallback");
|
||||
}
|
||||
}
|
||||
|
||||
private static class TwoSpecificConstructorsComponent {
|
||||
|
||||
private final Integer counter;
|
||||
|
||||
public TwoSpecificConstructorsComponent(Integer counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
public TwoSpecificConstructorsComponent(String name) {
|
||||
this(Integer.valueOf(name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -89,19 +89,17 @@ public class AutowiredConfigurationTests {
|
|||
assertThat(context.getBean(TestBean.class).getName(), equalTo(""));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Autowired} constructors are not supported on {@link Configuration} classes
|
||||
* due to CGLIB constraints
|
||||
*/
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void testAutowiredConfigurationConstructorsAreNotSupported() {
|
||||
DefaultListableBeanFactory context = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(context).loadBeanDefinitions(
|
||||
@Test
|
||||
public void testAutowiredConfigurationConstructorsAreSupported() {
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(factory).loadBeanDefinitions(
|
||||
new ClassPathResource("annotation-config.xml", AutowiredConstructorConfig.class));
|
||||
GenericApplicationContext ctx = new GenericApplicationContext(context);
|
||||
GenericApplicationContext ctx = new GenericApplicationContext(factory);
|
||||
ctx.registerBeanDefinition("config1", new RootBeanDefinition(AutowiredConstructorConfig.class));
|
||||
ctx.registerBeanDefinition("config2", new RootBeanDefinition(ColorConfig.class));
|
||||
ctx.refresh(); // should throw
|
||||
ctx.refresh();
|
||||
assertSame(ctx.getBean(AutowiredConstructorConfig.class).colour,
|
||||
ctx.getBean(Colour.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue