diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java index 0cbafed3f2..44529d3872 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java @@ -32,6 +32,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -124,6 +125,12 @@ public class EntityScanPackages { */ static class Registrar implements ImportBeanDefinitionRegistrar { + private final Environment environment; + + Registrar(Environment environment) { + this.environment = environment; + } + @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, getPackagesToScan(metadata)); @@ -132,11 +139,12 @@ public class EntityScanPackages { private Set getPackagesToScan(AnnotationMetadata metadata) { AnnotationAttributes attributes = AnnotationAttributes .fromMap(metadata.getAnnotationAttributes(EntityScan.class.getName())); - String[] basePackages = attributes.getStringArray("basePackages"); - Class[] basePackageClasses = attributes.getClassArray("basePackageClasses"); - Set packagesToScan = new LinkedHashSet<>(Arrays.asList(basePackages)); - for (Class basePackageClass : basePackageClasses) { - packagesToScan.add(ClassUtils.getPackageName(basePackageClass)); + Set packagesToScan = new LinkedHashSet<>(); + for (String basePackage : attributes.getStringArray("basePackages")) { + addResolvedPackage(basePackage, packagesToScan); + } + for (Class basePackageClass : attributes.getClassArray("basePackageClasses")) { + addResolvedPackage(ClassUtils.getPackageName(basePackageClass), packagesToScan); } if (packagesToScan.isEmpty()) { String packageName = ClassUtils.getPackageName(metadata.getClassName()); @@ -146,6 +154,10 @@ public class EntityScanPackages { return packagesToScan; } + private void addResolvedPackage(String packageName, Set packagesToScan) { + packagesToScan.add(this.environment.resolvePlaceholders(packageName)); + } + } static class EntityScanPackagesBeanDefinition extends GenericBeanDefinition { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScannerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScannerTests.java index 4b6ca9e4a8..47edd3875e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScannerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScannerTests.java @@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.domain.scan.b.EmbeddableB; import org.springframework.boot.autoconfigure.domain.scan.b.EntityB; import org.springframework.boot.autoconfigure.domain.scan.c.EmbeddableC; import org.springframework.boot.autoconfigure.domain.scan.c.EntityC; +import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; @@ -66,6 +67,19 @@ class EntityScannerTests { context.close(); } + @Test + void scanShouldScanFromResolvedPlaceholderPackage() throws Exception { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + TestPropertyValues.of("com.example.entity-package=org.springframework.boot.autoconfigure.domain.scan") + .applyTo(context); + context.register(ScanPlaceholderConfig.class); + context.refresh(); + EntityScanner scanner = new EntityScanner(context); + Set> scanned = scanner.scan(Entity.class); + assertThat(scanned).containsOnly(EntityA.class, EntityB.class, EntityC.class); + context.close(); + } + @Test void scanShouldScanFromMultiplePackages() throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanAConfig.class, @@ -141,4 +155,10 @@ class EntityScannerTests { } + @Configuration(proxyBeanMethods = false) + @EntityScan("${com.example.entity-package}") + static class ScanPlaceholderConfig { + + } + }