Support Ant-style package name with component index
This commit improves the component index so that it supports ant-style package name (i.e. com.example.**.foo). Issue: SPR-16152
This commit is contained in:
parent
9649b0cb25
commit
1838ddb95d
|
|
@ -22,6 +22,8 @@ import java.util.Properties;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
|
|
@ -46,7 +48,9 @@ import org.springframework.util.MultiValueMap;
|
|||
*/
|
||||
public class CandidateComponentsIndex {
|
||||
|
||||
private final MultiValueMap<String, String> index;
|
||||
private final static AntPathMatcher pathMatcher = new AntPathMatcher(".");
|
||||
|
||||
private final MultiValueMap<String, Entry> index;
|
||||
|
||||
|
||||
CandidateComponentsIndex(List<Properties> content) {
|
||||
|
|
@ -62,26 +66,47 @@ public class CandidateComponentsIndex {
|
|||
* or an empty set if none has been found for the specified {@code basePackage}
|
||||
*/
|
||||
public Set<String> getCandidateTypes(String basePackage, String stereotype) {
|
||||
List<String> candidates = this.index.get(stereotype);
|
||||
List<Entry> candidates = this.index.get(stereotype);
|
||||
if (candidates != null) {
|
||||
return candidates.parallelStream()
|
||||
.filter(t -> t.startsWith(basePackage))
|
||||
.filter(t -> t.match(basePackage))
|
||||
.map(t -> t.type)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
private static MultiValueMap<String, String> parseIndex(List<Properties> content) {
|
||||
MultiValueMap<String, String> index = new LinkedMultiValueMap<>();
|
||||
private static MultiValueMap<String, Entry> parseIndex(List<Properties> content) {
|
||||
MultiValueMap<String, Entry> index = new LinkedMultiValueMap<>();
|
||||
for (Properties entry : content) {
|
||||
entry.forEach((type, values) -> {
|
||||
String[] stereotypes = ((String) values).split(",");
|
||||
for (String stereotype : stereotypes) {
|
||||
index.add(stereotype, (String) type);
|
||||
index.add(stereotype, new Entry((String) type));
|
||||
}
|
||||
});
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private static class Entry {
|
||||
private final String type;
|
||||
private final String packageName;
|
||||
|
||||
Entry(String type) {
|
||||
this.type = type;
|
||||
this.packageName = ClassUtils.getPackageName(type);
|
||||
}
|
||||
|
||||
public boolean match(String basePackage) {
|
||||
if (pathMatcher.isPattern(basePackage)) {
|
||||
return pathMatcher.match(basePackage, this.packageName);
|
||||
}
|
||||
else {
|
||||
return this.type.startsWith(basePackage);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2002-2017 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 example.scannable.sub;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Component
|
||||
public class BarComponent {
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
|
@ -36,6 +36,7 @@ import example.scannable.NamedStubDao;
|
|||
import example.scannable.ScopedProxyTestBean;
|
||||
import example.scannable.ServiceInvocationCounter;
|
||||
import example.scannable.StubFooDao;
|
||||
import example.scannable.sub.BarComponent;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
@ -98,7 +99,31 @@ public class ClassPathScanningCandidateComponentProviderTests {
|
|||
assertTrue(containsBeanClass(candidates, StubFooDao.class));
|
||||
assertTrue(containsBeanClass(candidates, NamedStubDao.class));
|
||||
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
||||
assertEquals(6, candidates.size());
|
||||
assertTrue(containsBeanClass(candidates, BarComponent.class));
|
||||
assertEquals(7, candidates.size());
|
||||
assertBeanDefinitionType(candidates, expectedBeanDefinitionType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antStylePackageWithScan() {
|
||||
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
|
||||
provider.setResourceLoader(new DefaultResourceLoader(
|
||||
CandidateComponentsTestClassLoader.disableIndex(getClass().getClassLoader())));
|
||||
testAntStyle(provider, ScannedGenericBeanDefinition.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antStylePackageWithIndex() {
|
||||
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
|
||||
provider.setResourceLoader(new DefaultResourceLoader(TEST_BASE_CLASSLOADER));
|
||||
testAntStyle(provider, AnnotatedGenericBeanDefinition.class);
|
||||
}
|
||||
|
||||
private void testAntStyle(ClassPathScanningCandidateComponentProvider provider,
|
||||
Class<? extends BeanDefinition> expectedBeanDefinitionType) {
|
||||
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE + ".**.sub");
|
||||
assertTrue(containsBeanClass(candidates, BarComponent.class));
|
||||
assertEquals(1, candidates.size());
|
||||
assertBeanDefinitionType(candidates, expectedBeanDefinitionType);
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +225,8 @@ public class ClassPathScanningCandidateComponentProviderTests {
|
|||
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
|
||||
assertTrue(containsBeanClass(candidates, NamedComponent.class));
|
||||
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
||||
assertEquals(2, candidates.size());
|
||||
assertTrue(containsBeanClass(candidates, BarComponent.class));
|
||||
assertEquals(3, candidates.size());
|
||||
assertBeanDefinitionType(candidates, expectedBeanDefinitionType);
|
||||
}
|
||||
|
||||
|
|
@ -251,7 +277,8 @@ public class ClassPathScanningCandidateComponentProviderTests {
|
|||
assertTrue(containsBeanClass(candidates, FooServiceImpl.class));
|
||||
assertTrue(containsBeanClass(candidates, StubFooDao.class));
|
||||
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
||||
assertEquals(3, candidates.size());
|
||||
assertTrue(containsBeanClass(candidates, BarComponent.class));
|
||||
assertEquals(4, candidates.size());
|
||||
assertBeanDefinitionType(candidates, expectedBeanDefinitionType);
|
||||
}
|
||||
|
||||
|
|
@ -270,9 +297,10 @@ public class ClassPathScanningCandidateComponentProviderTests {
|
|||
provider.addExcludeFilter(new AnnotationTypeFilter(Service.class));
|
||||
provider.addExcludeFilter(new AnnotationTypeFilter(Controller.class));
|
||||
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
|
||||
assertEquals(2, candidates.size());
|
||||
assertEquals(3, candidates.size());
|
||||
assertTrue(containsBeanClass(candidates, NamedComponent.class));
|
||||
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
||||
assertTrue(containsBeanClass(candidates, BarComponent.class));
|
||||
assertFalse(containsBeanClass(candidates, FooServiceImpl.class));
|
||||
assertFalse(containsBeanClass(candidates, StubFooDao.class));
|
||||
assertFalse(containsBeanClass(candidates, NamedStubDao.class));
|
||||
|
|
@ -311,10 +339,11 @@ public class ClassPathScanningCandidateComponentProviderTests {
|
|||
provider.addIncludeFilter(new AnnotationTypeFilter(Component.class));
|
||||
provider.addIncludeFilter(new AssignableTypeFilter(FooServiceImpl.class));
|
||||
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
|
||||
assertEquals(6, candidates.size());
|
||||
assertEquals(7, candidates.size());
|
||||
assertTrue(containsBeanClass(candidates, NamedComponent.class));
|
||||
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
||||
assertTrue(containsBeanClass(candidates, FooServiceImpl.class));
|
||||
assertTrue(containsBeanClass(candidates, BarComponent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -324,9 +353,10 @@ public class ClassPathScanningCandidateComponentProviderTests {
|
|||
provider.addIncludeFilter(new AssignableTypeFilter(FooServiceImpl.class));
|
||||
provider.addExcludeFilter(new AssignableTypeFilter(FooService.class));
|
||||
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
|
||||
assertEquals(5, candidates.size());
|
||||
assertEquals(6, candidates.size());
|
||||
assertTrue(containsBeanClass(candidates, NamedComponent.class));
|
||||
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
||||
assertTrue(containsBeanClass(candidates, BarComponent.class));
|
||||
assertFalse(containsBeanClass(candidates, FooServiceImpl.class));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ example.scannable.FooServiceImpl=org.springframework.stereotype.Component,exampl
|
|||
example.scannable.ScopedProxyTestBean=example.scannable.FooService
|
||||
example.scannable.StubFooDao=org.springframework.stereotype.Component
|
||||
example.scannable.NamedStubDao=org.springframework.stereotype.Component
|
||||
example.scannable.ServiceInvocationCounter=org.springframework.stereotype.Component
|
||||
example.scannable.ServiceInvocationCounter=org.springframework.stereotype.Component
|
||||
example.scannable.sub.BarComponent=org.springframework.stereotype.Component
|
||||
Loading…
Reference in New Issue