Log warning if scanning org or org.springframework
Update ConfigurationWarningsApplicationContextInitializer to also log warnings if the user is scanning `org` or `org.springframework`. Fixes gh-4777
This commit is contained in:
parent
9be4b57182
commit
19056a1104
|
@ -16,6 +16,13 @@
|
|||
|
||||
package org.springframework.boot.context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
@ -33,7 +40,6 @@ import org.springframework.core.PriorityOrdered;
|
|||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
@ -60,7 +66,7 @@ public class ConfigurationWarningsApplicationContextInitializer
|
|||
* @return the checks to apply
|
||||
*/
|
||||
protected Check[] getChecks() {
|
||||
return new Check[] { new ComponentScanDefaultPackageCheck() };
|
||||
return new Check[] { new ComponentScanPackageCheck() };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,62 +126,101 @@ public class ConfigurationWarningsApplicationContextInitializer
|
|||
}
|
||||
|
||||
/**
|
||||
* {@link Check} for {@code @ComponentScan} on the default package.
|
||||
* {@link Check} for {@code @ComponentScan} on problematic package.
|
||||
*/
|
||||
protected static class ComponentScanDefaultPackageCheck implements Check {
|
||||
protected static class ComponentScanPackageCheck implements Check {
|
||||
|
||||
private static final Set<String> PROBLEM_PACKAGES;
|
||||
|
||||
static {
|
||||
Set<String> pacakges = new HashSet<String>();
|
||||
pacakges.add("org.springframework");
|
||||
pacakges.add("org");
|
||||
PROBLEM_PACKAGES = Collections.unmodifiableSet(pacakges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWarning(BeanDefinitionRegistry registry) {
|
||||
if (isComponentScanningDefaultPackage(registry)) {
|
||||
return "Your ApplicationContext is unlikely to start due to a "
|
||||
+ "@ComponentScan of the default package.";
|
||||
Set<String> scannedPackages = getComponentScanningPackages(registry);
|
||||
List<String> problematicPackages = getProblematicPackages(scannedPackages);
|
||||
if (problematicPackages.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return "Your ApplicationContext is unlikely to "
|
||||
+ "start due to a @ComponentScan of "
|
||||
+ StringUtils.collectionToDelimitedString(problematicPackages, ", ")
|
||||
+ ".";
|
||||
}
|
||||
|
||||
private boolean isComponentScanningDefaultPackage(
|
||||
protected Set<String> getComponentScanningPackages(
|
||||
BeanDefinitionRegistry registry) {
|
||||
Set<String> packages = new LinkedHashSet<String>();
|
||||
String[] names = registry.getBeanDefinitionNames();
|
||||
for (String name : names) {
|
||||
BeanDefinition definition = registry.getBeanDefinition(name);
|
||||
if (definition instanceof AnnotatedBeanDefinition) {
|
||||
AnnotatedBeanDefinition annotatedDefinition = (AnnotatedBeanDefinition) definition;
|
||||
if (isScanningDefaultPackage(annotatedDefinition.getMetadata())) {
|
||||
return true;
|
||||
}
|
||||
addComponentScanningPackages(packages,
|
||||
annotatedDefinition.getMetadata());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return packages;
|
||||
}
|
||||
|
||||
private boolean isScanningDefaultPackage(AnnotationMetadata metadata) {
|
||||
private void addComponentScanningPackages(Set<String> packages,
|
||||
AnnotationMetadata metadata) {
|
||||
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata
|
||||
.getAnnotationAttributes(ComponentScan.class.getName(), true));
|
||||
if (attributes != null && hasNoScanPackageSpecified(attributes)) {
|
||||
if (isInDefaultPackage(metadata.getClassName())) {
|
||||
return true;
|
||||
if (attributes != null) {
|
||||
addPackages(packages, attributes.getStringArray("value"));
|
||||
addPackages(packages, attributes.getStringArray("basePackages"));
|
||||
addClasses(packages, attributes.getStringArray("basePackageClasses"));
|
||||
if (packages.isEmpty()) {
|
||||
packages.add(ClassUtils.getPackageName(metadata.getClassName()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasNoScanPackageSpecified(AnnotationAttributes attributes) {
|
||||
return isAllEmpty(attributes, "value", "basePackages", "basePackageClasses");
|
||||
}
|
||||
|
||||
private boolean isAllEmpty(AnnotationAttributes attributes, String... names) {
|
||||
for (String name : names) {
|
||||
if (!ObjectUtils.isEmpty(attributes.getStringArray(name))) {
|
||||
return false;
|
||||
private void addPackages(Set<String> packages, String[] values) {
|
||||
if (values != null) {
|
||||
for (String value : values) {
|
||||
packages.add(value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isInDefaultPackage(String className) {
|
||||
String packageName = ClassUtils.getPackageName(className);
|
||||
return StringUtils.isEmpty(packageName);
|
||||
private void addClasses(Set<String> packages, String[] values) {
|
||||
if (values != null) {
|
||||
for (String value : values) {
|
||||
packages.add(ClassUtils.getPackageName(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getProblematicPackages(Set<String> scannedPackages) {
|
||||
List<String> problematicPackages = new ArrayList<String>();
|
||||
for (String scannedPackage : scannedPackages) {
|
||||
if (isProblematicPackage(scannedPackage)) {
|
||||
problematicPackages.add(getDisplayName(scannedPackage));
|
||||
}
|
||||
}
|
||||
return problematicPackages;
|
||||
}
|
||||
|
||||
private boolean isProblematicPackage(String scannedPackage) {
|
||||
if (scannedPackage == null || scannedPackage.length() == 0) {
|
||||
return true;
|
||||
}
|
||||
return PROBLEM_PACKAGES.contains(scannedPackage);
|
||||
}
|
||||
|
||||
private String getDisplayName(String scannedPackage) {
|
||||
if (scannedPackage == null || scannedPackage.length() == 0) {
|
||||
return "the default package";
|
||||
}
|
||||
return "'" + scannedPackage + "'";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,17 +16,23 @@
|
|||
|
||||
package org.springframework.boot.context;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.ComponentScanDefaultPackageCheck;
|
||||
import org.springframework.boot.context.configwarnings.InDefaultPackageConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.InDefaultPackageWithBasePackageClassesConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.InDefaultPackageWithBasePackagesConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.InDefaultPackageWithMetaAnnotationConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.InDefaultPackageWithValueConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.InDefaultPackageWithoutScanConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.InRealPackageConfiguration;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.ComponentScanPackageCheck;
|
||||
import org.springframework.boot.context.configwarnings.dflt.InDefaultPackageConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.dflt.InDefaultPackageWithBasePackageClassesConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.dflt.InDefaultPackageWithBasePackagesConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.dflt.InDefaultPackageWithMetaAnnotationConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.dflt.InDefaultPackageWithValueConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.dflt.InDefaultPackageWithoutScanConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.orgspring.InOrgSpringPackageConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.real.InRealButScanningProblemPackages;
|
||||
import org.springframework.boot.context.configwarnings.real.InRealPackageConfiguration;
|
||||
import org.springframework.boot.test.OutputCapture;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
|
@ -41,8 +47,11 @@ import static org.junit.Assert.assertThat;
|
|||
*/
|
||||
public class ConfigurationWarningsApplicationContextInitializerTests {
|
||||
|
||||
private static final String SCAN_WARNING = "Your ApplicationContext is unlikely to "
|
||||
+ "start due to a @ComponentScan of the default package";
|
||||
private static final String DEFAULT_SCAN_WARNING = "Your ApplicationContext is unlikely to "
|
||||
+ "start due to a @ComponentScan of the default package.";
|
||||
|
||||
private static final String ORGSPRING_SCAN_WARNING = "Your ApplicationContext is unlikely to "
|
||||
+ "start due to a @ComponentScan of 'org.springframework'.";
|
||||
|
||||
@Rule
|
||||
public OutputCapture output = new OutputCapture();
|
||||
|
@ -50,43 +59,58 @@ public class ConfigurationWarningsApplicationContextInitializerTests {
|
|||
@Test
|
||||
public void logWarningInDefaultPackage() {
|
||||
load(InDefaultPackageConfiguration.class);
|
||||
assertThat(this.output.toString(), containsString(SCAN_WARNING));
|
||||
assertThat(this.output.toString(), containsString(DEFAULT_SCAN_WARNING));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logWarningInDefaultPackageAndMetaAnnotation() {
|
||||
load(InDefaultPackageWithMetaAnnotationConfiguration.class);
|
||||
assertThat(this.output.toString(), containsString(SCAN_WARNING));
|
||||
assertThat(this.output.toString(), containsString(DEFAULT_SCAN_WARNING));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noLogIfInRealPackage() throws Exception {
|
||||
load(InRealPackageConfiguration.class);
|
||||
assertThat(this.output.toString(), not(containsString(SCAN_WARNING)));
|
||||
assertThat(this.output.toString(), not(containsString(DEFAULT_SCAN_WARNING)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noLogWithoutComponentScanAnnotation() throws Exception {
|
||||
load(InDefaultPackageWithoutScanConfiguration.class);
|
||||
assertThat(this.output.toString(), not(containsString(SCAN_WARNING)));
|
||||
assertThat(this.output.toString(), not(containsString(DEFAULT_SCAN_WARNING)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noLogIfHasValue() throws Exception {
|
||||
load(InDefaultPackageWithValueConfiguration.class);
|
||||
assertThat(this.output.toString(), not(containsString(SCAN_WARNING)));
|
||||
assertThat(this.output.toString(), not(containsString(DEFAULT_SCAN_WARNING)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noLogIfHasBasePackages() throws Exception {
|
||||
load(InDefaultPackageWithBasePackagesConfiguration.class);
|
||||
assertThat(this.output.toString(), not(containsString(SCAN_WARNING)));
|
||||
assertThat(this.output.toString(), not(containsString(DEFAULT_SCAN_WARNING)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noLogIfHasBasePackageClasses() throws Exception {
|
||||
load(InDefaultPackageWithBasePackageClassesConfiguration.class);
|
||||
assertThat(this.output.toString(), not(containsString(SCAN_WARNING)));
|
||||
assertThat(this.output.toString(), not(containsString(DEFAULT_SCAN_WARNING)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logWarningInOrgSpringPackage() {
|
||||
load(InOrgSpringPackageConfiguration.class);
|
||||
assertThat(this.output.toString(), containsString(ORGSPRING_SCAN_WARNING));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logWarningIfScanningProblemPackages() throws Exception {
|
||||
load(InRealButScanningProblemPackages.class);
|
||||
assertThat(this.output.toString(),
|
||||
containsString("Your ApplicationContext is unlikely to start due to a "
|
||||
+ "@ComponentScan of the default package, 'org.springframework'."));
|
||||
|
||||
}
|
||||
|
||||
private void load(Class<?> configClass) {
|
||||
|
@ -121,12 +145,25 @@ public class ConfigurationWarningsApplicationContextInitializerTests {
|
|||
* Testable ComponentScanDefaultPackageCheck that doesn't need to use the default
|
||||
* package.
|
||||
*/
|
||||
static class TestComponentScanDefaultPackageCheck
|
||||
extends ComponentScanDefaultPackageCheck {
|
||||
static class TestComponentScanDefaultPackageCheck extends ComponentScanPackageCheck {
|
||||
|
||||
@Override
|
||||
protected boolean isInDefaultPackage(String className) {
|
||||
return className.contains("InDefault");
|
||||
protected Set<String> getComponentScanningPackages(
|
||||
BeanDefinitionRegistry registry) {
|
||||
Set<String> scannedPackages = super.getComponentScanningPackages(registry);
|
||||
Set<String> result = new LinkedHashSet<String>();
|
||||
for (String scannedPackage : scannedPackages) {
|
||||
if (scannedPackage.endsWith("dflt")) {
|
||||
result.add("");
|
||||
}
|
||||
if (scannedPackage.endsWith("orgspring")) {
|
||||
result.add("org.springframework");
|
||||
}
|
||||
else {
|
||||
result.add(scannedPackage);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.context.configwarnings;
|
||||
package org.springframework.boot.context.configwarnings.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.context.configwarnings;
|
||||
package org.springframework.boot.context.configwarnings.dflt;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.context.configwarnings;
|
||||
package org.springframework.boot.context.configwarnings.dflt;
|
||||
|
||||
import org.springframework.boot.context.configwarnings.nested.ExampleBean;
|
||||
import org.springframework.boot.context.configwarnings.real.nested.ExampleBean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.context.configwarnings;
|
||||
package org.springframework.boot.context.configwarnings.dflt;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
|
@ -14,8 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.context.configwarnings;
|
||||
package org.springframework.boot.context.configwarnings.dflt;
|
||||
|
||||
import org.springframework.boot.context.configwarnings.annotation.MetaComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.context.configwarnings;
|
||||
package org.springframework.boot.context.configwarnings.dflt;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.context.configwarnings;
|
||||
package org.springframework.boot.context.configwarnings.dflt;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2012-2014 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.boot.context.configwarnings.orgspring;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
public class InOrgSpringPackageConfiguration {
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2012-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.boot.context.configwarnings.real;
|
||||
|
||||
import org.springframework.boot.context.configwarnings.dflt.InDefaultPackageConfiguration;
|
||||
import org.springframework.boot.context.configwarnings.orgspring.InOrgSpringPackageConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = { InDefaultPackageConfiguration.class,
|
||||
InOrgSpringPackageConfiguration.class })
|
||||
public class InRealButScanningProblemPackages {
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.context.configwarnings;
|
||||
package org.springframework.boot.context.configwarnings.real;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.context.configwarnings.nested;
|
||||
package org.springframework.boot.context.configwarnings.real.nested;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
Loading…
Reference in New Issue