How to copy Spring's component-scan
I want to search for some annotations in a Spring based web application, like @Entity
. Therefore I need the same functionality like Spring involves when the server starts up and it looks for all classes that are annotated with @Component
. In my case I don't create singleton's, it's just import开发者_Go百科ant for me to collect all those classes annotated with @Entity
.
Is there any possibility to use existing Spring tools for that? I want to search exactly in the same namespace as Spring does for the @Component
annotations.
Sure, look at parse()
method in org.springframework.context.annotation.ComponentScanBeanDefinitionParser
. This method is called when Spring encounters <context:component-scan/>
in the XML configuration. Probably You can strip it a bit to better suit your needs, but it should serve as a comprehensive example.
The class You should be particularly interested in is org.springframework.context.annotation.ClassPathBeanDefinitionScanner
. From JavaDoc:
Candidate classes are detected through configurable type filters. The default filters include classes that are annotated with Spring's @Component, @Repository, @Service, or @Controller stereotype.
BTW if you need less general solution, maybe your persistence provider has some API to fetch all entity classes?
Spring's built-in classpath scanning infrastructure (ClassPathBeanDefinitionScanner/ ComponentScanBeanDefinitionParser) is geared up for registering classes as BeanDefinitions within an Spring appcontext.
If you're just looking to obtain a list of classes annotated with a given annotation (rather than actually register them in Spring as bean definitions) take a look at the Google Reflections library.
Reflections allows you to scan your classpath using various filters, including an annotation filter.
Reflections reflections = new Reflections("my.project.prefix");
Set<Class<? extends SomeClassOrInterface>> subTypes = reflections.getSubTypesOf(SomeClassOrInterface.class);
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class);
Spring based solution
- Use spring AnnotationTypeFilter and pass Entity.class as annotationType
- using ResourcePatternResolver load all resouces(.class) under given pacakage
- Use SimpleMetadataReaderFactory to get MetadataReader
- for each resource you can call match on AnnotationTypeFilter using MetadataReader
- metadataReader.getAnnotationMetadata().getClassName() will provide FQN of class
usage
AnnotatedClassFinder entityScanner = new AnnotatedClassFinder(Entity.class);
entityScanner.setPackages(Arrays.asList("org.myapp.domain"));
Collection<Class<?>> entities = entityScanner.findMarkedClassOfType();
public class AnnotatedClassFinder {
private static final String CLASS_RESOURCE_PATTERN = "**/*.class";
private List<String> packages;
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
private final ResourcePatternResolver resourcePatternResolver = ResourcePatternUtils
.getResourcePatternResolver(resourceLoader);
private final MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
private final TypeFilter annotationFilter;
public AnnotatedClassFinder(final Class<? extends Annotation> annotationToScanFor) {
annotationFilter = new AnnotationTypeFilter(annotationToScanFor);
}
public Set<Class<?>> findMarkedClassOfType() {
if (packages == null) {
return new HashSet<Class<?>>();
}
final Set<Class<?>> annotatedClasses = new HashSet<Class<?>>();
try {
for (final String p : packages) {
final String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(p)) + "/"
+ CLASS_RESOURCE_PATTERN;
final Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
for (final Resource resource : resources) {
if (resource.isReadable()) {
final MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (annotationFilter.match(metadataReader, metadataReaderFactory)) {
annotatedClasses.add(Class.forName(metadataReader.getAnnotationMetadata().getClassName()));
}
}
}
}
return annotatedClasses;
} catch (final IOException ex) {
throw new RuntimeException("I/O failure during classpath scanning", ex);
} catch (final ClassNotFoundException ex) {
throw new RuntimeException("Class loading failure during classpath scanning", ex);
}
}
public void setPackages(final List<String> packages) {
this.packages = packages;
}
}
精彩评论