Allow beans without public constructors to load
Allow `BeanDefinitionLoader` to load classes that don't have public
constructors. The constraint was first introduced in d82c50804f to
solve an issue with anonymous Groovy classes but causes particular
problems with `@SpringBootTest`.
See gh-20929
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
@@ -31,8 +31,6 @@ import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -41,7 +39,6 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -53,6 +50,7 @@ import org.springframework.util.StringUtils;
|
||||
* {@link SpringApplication} for the types of sources that are supported.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Vladislav Kisel
|
||||
* @see #setBeanNameGenerator(BeanNameGenerator)
|
||||
*/
|
||||
class BeanDefinitionLoader {
|
||||
@@ -273,16 +271,14 @@ class BeanDefinitionLoader {
|
||||
return Package.getPackage(source.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the bean is eligible for registration.
|
||||
* @param type candidate bean type
|
||||
* @return true if the given bean type is eligible for registration, i.e. not a groovy
|
||||
* closure nor an anonymous class
|
||||
*/
|
||||
private boolean isComponent(Class<?> type) {
|
||||
// This has to be a bit of a guess. The only way to be sure that this type is
|
||||
// eligible is to make a bean definition out of it and try to instantiate it.
|
||||
if (MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).isPresent(Component.class)) {
|
||||
return true;
|
||||
}
|
||||
// Nested anonymous classes are not eligible for registration, nor are groovy
|
||||
// closures
|
||||
return !type.getName().matches(".*\\$_.*closure.*") && !type.isAnonymousClass()
|
||||
&& type.getConstructors() != null && type.getConstructors().length != 0;
|
||||
return !type.getName().matches(".*\\$_.*closure.*") && !type.isAnonymousClass();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test;
|
||||
import sampleconfig.MyComponentInPackageWithoutDot;
|
||||
|
||||
import org.springframework.boot.sampleconfig.MyComponent;
|
||||
import org.springframework.boot.sampleconfig.MyNamedComponent;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
@@ -31,6 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Tests for {@link BeanDefinitionLoader}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Vladislav Kisel
|
||||
*/
|
||||
class BeanDefinitionLoaderTests {
|
||||
|
||||
@@ -53,6 +55,21 @@ class BeanDefinitionLoaderTests {
|
||||
assertThat(this.registry.containsBean("myComponent")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void anonymousClassNotLoaded() {
|
||||
MyComponent myComponent = new MyComponent() {
|
||||
};
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry, myComponent.getClass());
|
||||
assertThat(loader.load()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadJsr330Class() {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry, MyNamedComponent.class);
|
||||
assertThat(loader.load()).isEqualTo(1);
|
||||
assertThat(this.registry.containsBean("myNamedComponent")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadXmlResource() {
|
||||
ClassPathResource resource = new ClassPathResource("sample-beans.xml", getClass());
|
||||
@@ -83,8 +100,9 @@ class BeanDefinitionLoaderTests {
|
||||
@Test
|
||||
void loadPackage() {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry, MyComponent.class.getPackage());
|
||||
assertThat(loader.load()).isEqualTo(1);
|
||||
assertThat(loader.load()).isEqualTo(2);
|
||||
assertThat(this.registry.containsBean("myComponent")).isTrue();
|
||||
assertThat(this.registry.containsBean("myNamedComponent")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,8 +131,9 @@ class BeanDefinitionLoaderTests {
|
||||
@Test
|
||||
void loadPackageName() {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry, MyComponent.class.getPackage().getName());
|
||||
assertThat(loader.load()).isEqualTo(1);
|
||||
assertThat(loader.load()).isEqualTo(2);
|
||||
assertThat(this.registry.containsBean("myComponent")).isTrue();
|
||||
assertThat(this.registry.containsBean("myNamedComponent")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -131,8 +150,9 @@ class BeanDefinitionLoaderTests {
|
||||
void loadPackageAndClassDoesNotDoubleAdd() {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry, MyComponent.class.getPackage(),
|
||||
MyComponent.class);
|
||||
assertThat(loader.load()).isEqualTo(1);
|
||||
assertThat(loader.load()).isEqualTo(2);
|
||||
assertThat(this.registry.containsBean("myComponent")).isTrue();
|
||||
assertThat(this.registry.containsBean("myNamedComponent")).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.boot.sampleconfig;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
@Named
|
||||
public class MyNamedComponent {
|
||||
|
||||
MyNamedComponent() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user