开发者

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;
    }

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜