Register all packages where @EnableAutoConfiguration is used

Previously, when @EnableAutoConfiguration was used in multiple packages,
the last @EnableAutoConfiguration that was processed would
win and only its package would be stored as an auto-configuration
package.

This commit updates AutoConfigurationPackages to allow multiple package
name registrations. AutoConfigurationPackages.set(…) has been altered to
augment the constructor arguments of the BeanDefinition registered for
the initial call to the method so that the packages handed to the method
call will be added to the bean definition and not replace the previous
ones. The method has been renamed register(…) to reflect the changed
behavior.

Closes gh-1994
This commit is contained in:
Oliver Gierke 2014-11-24 21:24:51 +01:00 committed by Andy Wilkinson
parent a83f9c6311
commit 123b90fa64
5 changed files with 129 additions and 15 deletions

View File

@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
@ -24,6 +25,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
@ -39,6 +42,7 @@ import org.springframework.util.StringUtils;
*
* @author Phillip Webb
* @author Dave Syer
* @author Oliver Gierke
*/
public abstract class AutoConfigurationPackages {
@ -75,21 +79,46 @@ public abstract class AutoConfigurationPackages {
}
/**
* Programmatically set the auto-configuration package names. You can use this method
* to manually define the base packages that will be used for a given
* {@link BeanDefinitionRegistry}. Generally it's recommended that you don't call this
* method directly, but instead rely on the default convention where the package name
* is set from your {@code @EnableAutoConfiguration} configuration class.
* Programmatically registers the auto-configuration package names. Subsequent
* invocations will add the given package names to those that have already been
* registered. You can use this method to manually define the base packages that will
* be used for a given {@link BeanDefinitionRegistry}. Generally it's recommended that
* you don't call this method directly, but instead rely on the default convention
* where the package name is set from your {@code @EnableAutoConfiguration}
* configuration class or classes.
* @param registry the bean definition registry
* @param packageNames the pacakge names to set
* @param packageNames the package names to set
*/
public static void set(BeanDefinitionRegistry registry, String... packageNames) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition
.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0,
augmentBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
private static String[] augmentBasePackages(
ConstructorArgumentValues constructorArguments, String[] packageNames) {
ValueHolder valueHolder = constructorArguments.getIndexedArgumentValue(0,
List.class);
List<String> packages = new ArrayList<String>(
Arrays.asList((String[]) valueHolder.getValue()));
packages.addAll(Arrays.asList(packageNames));
return packages.toArray(new String[packages.size()]);
}
/**
@ -102,7 +131,7 @@ public abstract class AutoConfigurationPackages {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
set(registry, ClassUtils.getPackageName(metadata.getClassName()));
register(registry, ClassUtils.getPackageName(metadata.getClassName()));
}
}

View File

@ -17,21 +17,28 @@
package org.springframework.boot.autoconfigure;
import java.util.Collections;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar;
import org.springframework.boot.autoconfigure.packages.one.FirstConfiguration;
import org.springframework.boot.autoconfigure.packages.two.SecondConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link AutoConfigurationPackages}.
*
* @author Phillip Webb
* @author Oliver Gierke
*/
@SuppressWarnings("resource")
public class AutoConfigurationPackagesTests {
@ -57,13 +64,37 @@ public class AutoConfigurationPackagesTests {
AutoConfigurationPackages.get(context.getBeanFactory());
}
@Test
public void detectsMultipleAutoConfigurationPackages() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
FirstConfiguration.class, SecondConfiguration.class);
List<String> packages = AutoConfigurationPackages.get(context.getBeanFactory());
assertThat(
packages,
hasItems(FirstConfiguration.class.getPackage().getName(),
SecondConfiguration.class.getPackage().getName()));
assertThat(packages, hasSize(2));
}
@Configuration
@Import(AutoConfigurationPackages.Registrar.class)
static class ConfigWithRegistrar {
}
@Configuration
static class EmptyConfig {
}
/**
* Test helper to allow {@link Registrar} to be referenced from other packages.
*/
public static class TestRegistrar extends Registrar {
}
}

View File

@ -39,7 +39,7 @@ public class TestAutoConfigurationPackageRegistrar implements
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata
.getAnnotationAttributes(TestAutoConfigurationPackage.class.getName(),
true));
AutoConfigurationPackages.set(registry,
AutoConfigurationPackages.register(registry,
ClassUtils.getPackageName(attributes.getString("value")));
}

View File

@ -0,0 +1,27 @@
/*
* 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.autoconfigure.packages.one;
import org.springframework.boot.autoconfigure.AutoConfigurationPackagesTests.TestRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(TestRegistrar.class)
public class FirstConfiguration {
}

View File

@ -0,0 +1,27 @@
/*
* 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.autoconfigure.packages.two;
import org.springframework.boot.autoconfigure.AutoConfigurationPackagesTests.TestRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(TestRegistrar.class)
public class SecondConfiguration {
}