Skip to content

Commit

Permalink
Allow beans without public constructors to load
Browse files Browse the repository at this point in the history
Allow `BeanDefinitionLoader` to load classes that don't have public
constructors. The constraint was first introduced in d82c508 to
solve an issue with anonymous Groovy classes but causes particular
problems with `@SpringBootTest`.

See gh-20929
  • Loading branch information
encircled authored and philwebb committed Jun 6, 2020
1 parent a0518d3 commit d8d8f9c
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -31,8 +31,6 @@
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;
Expand All @@ -41,7 +39,6 @@
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;
Expand All @@ -53,6 +50,7 @@
* {@link SpringApplication} for the types of sources that are supported.
*
* @author Phillip Webb
* @author Vladislav Kisel
* @see #setBeanNameGenerator(BeanNameGenerator)
*/
class BeanDefinitionLoader {
Expand Down Expand Up @@ -273,16 +271,14 @@ private Package findPackage(CharSequence source) {
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();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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;

Expand All @@ -31,6 +32,7 @@
* Tests for {@link BeanDefinitionLoader}.
*
* @author Phillip Webb
* @author Vladislav Kisel
*/
class BeanDefinitionLoaderTests {

Expand All @@ -53,6 +55,21 @@ void loadClass() {
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());
Expand Down Expand Up @@ -83,8 +100,9 @@ void loadGroovyResourceWithNamespace() {
@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
Expand Down Expand Up @@ -113,8 +131,9 @@ void loadGroovyName() {
@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
Expand All @@ -131,8 +150,9 @@ void loadPackageNameWithoutDot() {
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();
}

}
Original file line number Diff line number Diff line change
@@ -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() {

}

}

0 comments on commit d8d8f9c

Please sign in to comment.