Skip to content

Commit ce169c4

Browse files
committed
Allow to customize how EntityScanner scans entities
This commit adds a protected method that lets an override customize the configuration of the ClassPathScanningCandidateComponentProvider used to scan entities. Closes gh-23154
1 parent 446dfe4 commit ce169c4

File tree

2 files changed

+64
-5
lines changed

2 files changed

+64
-5
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanner.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -63,9 +63,8 @@ public final Set<Class<?>> scan(Class<? extends Annotation>... annotationTypes)
6363
if (packages.isEmpty()) {
6464
return Collections.emptySet();
6565
}
66-
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
67-
scanner.setEnvironment(this.context.getEnvironment());
68-
scanner.setResourceLoader(this.context);
66+
ClassPathScanningCandidateComponentProvider scanner = createClassPathScanningCandidateComponentProvider(
67+
this.context);
6968
for (Class<? extends Annotation> annotationType : annotationTypes) {
7069
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
7170
}
@@ -80,6 +79,22 @@ public final Set<Class<?>> scan(Class<? extends Annotation>... annotationTypes)
8079
return entitySet;
8180
}
8281

82+
/**
83+
* Create a {@link ClassPathScanningCandidateComponentProvider} to scan entities based
84+
* on the specified {@link ApplicationContext}.
85+
* @param context the {@link ApplicationContext} to use
86+
* @return a {@link ClassPathScanningCandidateComponentProvider} suitable to scan
87+
* entities
88+
* @since 2.4.0
89+
*/
90+
protected ClassPathScanningCandidateComponentProvider createClassPathScanningCandidateComponentProvider(
91+
ApplicationContext context) {
92+
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
93+
scanner.setEnvironment(context.getEnvironment());
94+
scanner.setResourceLoader(context);
95+
return scanner;
96+
}
97+
8398
private List<String> getPackages() {
8499
List<String> packages = EntityScanPackages.get(this.context).getPackageNames();
85100
if (packages.isEmpty() && AutoConfigurationPackages.has(this.context)) {

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScannerTests.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,24 +16,33 @@
1616

1717
package org.springframework.boot.autoconfigure.domain;
1818

19+
import java.util.Collections;
1920
import java.util.Set;
2021

2122
import javax.persistence.Embeddable;
2223
import javax.persistence.Entity;
2324

2425
import org.junit.jupiter.api.Test;
26+
import org.mockito.ArgumentCaptor;
2527

2628
import org.springframework.boot.autoconfigure.domain.scan.a.EmbeddableA;
2729
import org.springframework.boot.autoconfigure.domain.scan.a.EntityA;
2830
import org.springframework.boot.autoconfigure.domain.scan.b.EmbeddableB;
2931
import org.springframework.boot.autoconfigure.domain.scan.b.EntityB;
3032
import org.springframework.boot.autoconfigure.domain.scan.c.EmbeddableC;
3133
import org.springframework.boot.autoconfigure.domain.scan.c.EntityC;
34+
import org.springframework.context.ApplicationContext;
3235
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
36+
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
3337
import org.springframework.context.annotation.Configuration;
38+
import org.springframework.core.type.filter.AnnotationTypeFilter;
3439

3540
import static org.assertj.core.api.Assertions.assertThat;
3641
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
42+
import static org.mockito.BDDMockito.given;
43+
import static org.mockito.Mockito.mock;
44+
import static org.mockito.Mockito.verify;
45+
import static org.mockito.Mockito.verifyNoMoreInteractions;
3746

3847
/**
3948
* Tests for {@link EntityScanner}.
@@ -79,6 +88,41 @@ void scanShouldFilterOnAnnotation() throws Exception {
7988
context.close();
8089
}
8190

91+
@Test
92+
void scanShouldUseCustomCandidateComponentProvider() throws ClassNotFoundException {
93+
ClassPathScanningCandidateComponentProvider candidateComponentProvider = mock(
94+
ClassPathScanningCandidateComponentProvider.class);
95+
given(candidateComponentProvider.findCandidateComponents("org.springframework.boot.autoconfigure.domain.scan"))
96+
.willReturn(Collections.emptySet());
97+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanConfig.class);
98+
TestEntityScanner scanner = new TestEntityScanner(context, candidateComponentProvider);
99+
scanner.scan(Entity.class);
100+
ArgumentCaptor<AnnotationTypeFilter> annotationTypeFilter = ArgumentCaptor.forClass(AnnotationTypeFilter.class);
101+
verify(candidateComponentProvider).addIncludeFilter(annotationTypeFilter.capture());
102+
verify(candidateComponentProvider)
103+
.findCandidateComponents("org.springframework.boot.autoconfigure.domain.scan");
104+
verifyNoMoreInteractions(candidateComponentProvider);
105+
assertThat(annotationTypeFilter.getValue().getAnnotationType()).isEqualTo(Entity.class);
106+
}
107+
108+
private static class TestEntityScanner extends EntityScanner {
109+
110+
private final ClassPathScanningCandidateComponentProvider candidateComponentProvider;
111+
112+
TestEntityScanner(ApplicationContext context,
113+
ClassPathScanningCandidateComponentProvider candidateComponentProvider) {
114+
super(context);
115+
this.candidateComponentProvider = candidateComponentProvider;
116+
}
117+
118+
@Override
119+
protected ClassPathScanningCandidateComponentProvider createClassPathScanningCandidateComponentProvider(
120+
ApplicationContext context) {
121+
return this.candidateComponentProvider;
122+
}
123+
124+
}
125+
82126
@Configuration(proxyBeanMethods = false)
83127
@EntityScan("org.springframework.boot.autoconfigure.domain.scan")
84128
static class ScanConfig {

0 commit comments

Comments
 (0)