diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java index 56e38595d3c..e9cc06fe104 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java @@ -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 packages = new ArrayList( + 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())); } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationPackagesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationPackagesTests.java index bef0406decb..f22f52fc97b 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationPackagesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationPackagesTests.java @@ -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 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 { + } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationPackageRegistrar.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationPackageRegistrar.java index 31c8bac222e..3e5dcbe3d2a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationPackageRegistrar.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationPackageRegistrar.java @@ -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"))); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/one/FirstConfiguration.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/one/FirstConfiguration.java new file mode 100644 index 00000000000..618c8cedffe --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/one/FirstConfiguration.java @@ -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 { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/two/SecondConfiguration.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/two/SecondConfiguration.java new file mode 100644 index 00000000000..c8c648a4f33 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/two/SecondConfiguration.java @@ -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 { + +}