diff --git a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScanRegistrar.java b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScanRegistrar.java index 8a6a54e1ef..1113cdd6bc 100644 --- a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScanRegistrar.java +++ b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScanRegistrar.java @@ -16,8 +16,8 @@ package org.springframework.boot.orm.jpa; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; @@ -25,6 +25,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; +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; @@ -40,6 +41,7 @@ import org.springframework.util.ObjectUtils; * {@link ImportBeanDefinitionRegistrar} used by {@link EntityScan}. * * @author Phillip Webb + * @author Oliver Gierke */ class EntityScanRegistrar implements ImportBeanDefinitionRegistrar { @@ -48,31 +50,25 @@ class EntityScanRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + Set packagesToScan = getPackagesToScan(importingClassMetadata); if (!registry.containsBeanDefinition(BEAN_NAME)) { - GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); - beanDefinition.setBeanClass(EntityScanBeanPostProcessor.class); - beanDefinition.getConstructorArgumentValues().addGenericArgumentValue( - getPackagesToScan(importingClassMetadata)); - beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - // We don't need this one to be post processed otherwise it can cause a - // cascade of bean instantiation that we would rather avoid. - beanDefinition.setSynthetic(true); - registry.registerBeanDefinition(BEAN_NAME, beanDefinition); + addEntityScanBeanPostProcessor(registry, packagesToScan); + } + else { + updateEntityScanBeanPostProcessor(registry, packagesToScan); } } - private String[] getPackagesToScan(AnnotationMetadata metadata) { + private Set getPackagesToScan(AnnotationMetadata metadata) { AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata .getAnnotationAttributes(EntityScan.class.getName())); String[] value = attributes.getStringArray("value"); String[] basePackages = attributes.getStringArray("basePackages"); Class[] basePackageClasses = attributes.getClassArray("basePackageClasses"); - if (!ObjectUtils.isEmpty(value)) { Assert.state(ObjectUtils.isEmpty(basePackages), "@EntityScan basePackages and value attributes are mutually exclusive"); } - Set packagesToScan = new LinkedHashSet(); packagesToScan.addAll(Arrays.asList(value)); packagesToScan.addAll(Arrays.asList(basePackages)); @@ -80,10 +76,38 @@ class EntityScanRegistrar implements ImportBeanDefinitionRegistrar { packagesToScan.add(ClassUtils.getPackageName(basePackageClass)); } if (packagesToScan.isEmpty()) { - return new String[] { ClassUtils.getPackageName(metadata.getClassName()) }; + return Collections.singleton(ClassUtils.getPackageName(metadata + .getClassName())); } - return new ArrayList(packagesToScan).toArray(new String[packagesToScan - .size()]); + return packagesToScan; + } + + private void addEntityScanBeanPostProcessor(BeanDefinitionRegistry registry, + Set packagesToScan) { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(EntityScanBeanPostProcessor.class); + beanDefinition.getConstructorArgumentValues().addGenericArgumentValue( + toArray(packagesToScan)); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + // We don't need this one to be post processed otherwise it can cause a + // cascade of bean instantiation that we would rather avoid. + beanDefinition.setSynthetic(true); + registry.registerBeanDefinition(BEAN_NAME, beanDefinition); + } + + private void updateEntityScanBeanPostProcessor(BeanDefinitionRegistry registry, + Set packagesToScan) { + BeanDefinition definition = registry.getBeanDefinition(BEAN_NAME); + ValueHolder constructorArguments = definition.getConstructorArgumentValues() + .getGenericArgumentValue(String[].class); + Set mergedPackages = new LinkedHashSet(); + mergedPackages.addAll(Arrays.asList((String[]) constructorArguments.getValue())); + mergedPackages.addAll(packagesToScan); + constructorArguments.setValue(toArray(mergedPackages)); + } + + private String[] toArray(Set set) { + return set.toArray(new String[set.size()]); } /** diff --git a/spring-boot/src/test/java/org/springframework/boot/orm/jpa/EntityScanTests.java b/spring-boot/src/test/java/org/springframework/boot/orm/jpa/EntityScanTests.java index 5a8d9e0e3c..c2773c7dd8 100644 --- a/spring-boot/src/test/java/org/springframework/boot/orm/jpa/EntityScanTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/orm/jpa/EntityScanTests.java @@ -109,6 +109,13 @@ public class EntityScanTests { assertSetPackagesToScan("com.mycorp.entity"); } + @Test + public void considersMultipleEntityScanAnnotations() { + this.context = new AnnotationConfigApplicationContext(MultiScanFirst.class, + MultiScanSecond.class); + assertSetPackagesToScan("foo", "bar"); + } + private void assertSetPackagesToScan(String... expected) { String[] actual = this.context.getBean( TestLocalContainerEntityManagerFactoryBean.class).getPackagesToScan(); @@ -185,6 +192,16 @@ public class EntityScanTests { } } + @EntityScan(basePackages = "foo") + static class MultiScanFirst extends BaseConfig { + + } + + @EntityScan(basePackages = "bar") + static class MultiScanSecond extends BaseConfig { + + } + private static class TestLocalContainerEntityManagerFactoryBean extends LocalContainerEntityManagerFactoryBean {