如何复制Spring的组件扫描

时间:2011-03-17 10:14:31

标签: spring annotations

我想在基于Spring的Web应用程序中搜索一些注释,例如@Entity。因此,我需要像Spring在服务器启动时所涉及的功能,并查找所有使用@Component注释的类。在我的情况下,我不创建单例,对我来说收集所有用@Entity注释的类非常重要。

是否有可能使用现有的Spring工具?我想完全搜索与Spring对@Component注释相同的命名空间。

3 个答案:

答案 0 :(得分:5)

当然,请查看parse()中的org.springframework.context.annotation.ComponentScanBeanDefinitionParser方法。当Spring在XML配置中遇到<context:component-scan/>时,会调用此方法。可能你可以剥掉它以更好地满足你的需求,但它应该作为一个全面的例子。

你应该特别感兴趣的课程是org.springframework.context.annotation.ClassPathBeanDefinitionScanner。来自JavaDoc:

  

通过可配置的类型过滤器检测候选类。默认过滤器包括使用Spring的@Component,@ Repository,@ Service或@Controller构造型注释的类。

顺便说一句,如果您需要较少的通用解决方案,也许您的持久性提供程序有一些API来获取所有实体类?

答案 1 :(得分:4)

Spring的内置类路径扫描基础结构(ClassPathBeanDefinitionScanner / ComponentScanBeanDefinitionParser)已经准备就绪,可以在Spring appcontext中将类注册为BeanDefinition。

如果您只是想获取一个使用给定注释注释的类列表(而不是在Spring中将它们实际注册为bean定义),请查看Google Reflections库。

Reflections允许您使用各种过滤器扫描类路径,包括注释过滤器。

Reflections reflections = new Reflections("my.project.prefix");

Set<Class<? extends SomeClassOrInterface>> subTypes = reflections.getSubTypesOf(SomeClassOrInterface.class);
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class);

答案 2 :(得分:1)

基于Spring的解决方案

  • 使用spring AnnotationTypeFilter并将Entity.class作为annotationType传递
  • 使用ResourcePatternResolver加载给定pacakage下的所有资源(.class)
  • 使用SimpleMetadataReaderFactory获取MetadataReader
  • 您可以使用MetadataReader
  • 在AnnotationTypeFilter上调用匹配的每个资源
  • metadataReader.getAnnotationMetadata()。getClassName()将提供类的FQN

<强>使用

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

}