简体   繁体   English

如何使用 ClassPathScanningCandidateComponentProvider 查找具有指定注释的接口?

[英]How to find interfaces with specified annotation using ClassPathScanningCandidateComponentProvider?

I try to replace org.reflections:reflections library by org.springframework:spring-context and ClassPathScanningCandidateComponentProvider because reflections library has a bug that is critical for me.我尝试用 org.springframework:spring-context 和ClassPathScanningCandidateComponentProvider替换 org.reflections:reflections 库,因为反射库有一个对我来说至关重要的错误。 I want to find all classes, interfaces and subclasses that contain specific annotation.我想找到所有包含特定注释的类、接口和子类。 It was easy in reflections:反思很容易:

var subTypeScanner = new SubTypesScanner( true );
        var typeAnnotationScanner = new TypeAnnotationsScanner();
        var configBuilder = new ConfigurationBuilder().setUrls( ClasspathHelper.forPackage( "com.example" ) )
                .setScanners( subTypeScanner, typeAnnotationScanner );

        Reflections reflections= new Reflections( configBuilder );
        Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(Path.class);

Unfortunately it is not easy for me using ClassPathScanningCandidateComponentProvider .不幸的是,使用ClassPathScanningCandidateComponentProvider对我来说并不容易。 I have made workaround to find subclasses.我已经解决了寻找子类的方法。 First I find set of classes and after that I search for subclasses separately for each class:首先,我找到一组类,然后分别为每个 class 搜索子类:

protected Set< Class< ? > > findClasses( ClassPathScanningCandidateComponentProvider aScanner )
    {
        return aScanner.findCandidateComponents( "com.example" )
            .stream()
            .map( BeanDefinition::getBeanClassName )
            .map( e -> {
                try
                {
                    return Class.forName( e );
                }
                catch( ClassNotFoundException aE )
                {
                    throw new RuntimeException( aE );
                }
            } )
            .collect( Collectors.toUnmodifiableSet() );
    }


protected Set< Class< ? > > findSubClasses( Set< Class< ? > > aClasses )
    {
        return aClasses.stream()
                .map( aClass -> {
                    ClassPathScanningCandidateComponentProvider scanner =
                            new ClassPathScanningCandidateComponentProvider( false );
                    scanner.addIncludeFilter( new AssignableTypeFilter( aClass ) );
                    return scanner.findCandidateComponents( "com.example" );

                } )
                .flatMap(Collection::stream)
                .map( BeanDefinition::getBeanClassName )
                .map( e -> {
                    try
                    {
                        return Class.forName( e );
                    }
                    catch( ClassNotFoundException aE )
                    {
                        throw new RuntimeException( aE );
                    }
                } )
                .collect( Collectors.toUnmodifiableSet() );
    }

Unfortunately I do not know how to find interfaces with annotation.不幸的是,我不知道如何找到带有注释的接口。 Following code does not finds interfaces.以下代码找不到接口。 It only finds classes that has Path annotation or extends class or implements interface that has Path annotation.它只查找具有Path注释或扩展 class 或实现具有Path注释的接口的类。

ClassPathScanningCandidateComponentProvider scanner =
            new ClassPathScanningCandidateComponentProvider( false );
    scanner.addIncludeFilter( new AnnotationTypeFilter( Path.class, true, true ) );
    Set<Class<?>> classes = findClasses(scanner);
    //findSubClasses(findClasses( scanner ))

How to find interfaces with Path annotation?如何找到带有Path注释的接口?

The current implementation is also clear as mentioned on method level docs当前的实现也很清楚,如方法级别文档中所述

The default implementation checks whether the class is not an interface and not dependent on an enclosing class.默认实现检查 class 是否不是接口并且不依赖于封闭的 class。

As per this github issue we can fix this by overridden behaviour of isCandidateComponent() method根据这个 github 问题,我们可以通过覆盖isCandidateComponent()方法的行为来解决这个问题

Here is the sample reference working code from the github issue这是来自 github 问题的示例参考工作代码

  provider = new ClassPathScanningCandidateComponentProvider(false) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return super.isCandidateComponent(beanDefinition) || beanDefinition.getMetadata().isAbstract();
            }
        };

Annotation注解

package in.silentsudo.classpathannotationscanner.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyCustomAnnotation {
}

My Simple interface我的简单界面

package in.silentsudo.classpathannotationscanner;

import in.silentsudo.classpathannotationscanner.annotation.MyCustomAnnotation;

@MyCustomAnnotation
public interface SimpleInterface {
}

Sample test code示例测试代码

package in.silentsudo.classpathannotationscanner;

import in.silentsudo.classpathannotationscanner.annotation.MyCustomAnnotation;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

@RestController
@RequestMapping("/list")
public class SampleController {

    private final ClassPathScanningCandidateComponentProvider provider;

    SampleController() {
        provider = new ClassPathScanningCandidateComponentProvider(false) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return super.isCandidateComponent(beanDefinition) || beanDefinition.getMetadata().isAbstract();
            }
        };
        provider.addIncludeFilter(new AnnotationTypeFilter(MyCustomAnnotation.class, true, true));
    }

    @GetMapping
    public Map<String, Object> list() throws Exception {
        return Map.of("mycustomannotationsmarkedinterafce", getData());
    }

    public List<String> getData() throws Exception {
        final Set<BeanDefinition> classes = provider.findCandidateComponents("in.silentsudo.classpathannotationscanner");
        List<String> names = new ArrayList<>();
        for (BeanDefinition bean : classes) {
            Class<?> clazz = Class.forName(bean.getBeanClassName());
            names.add(clazz.getName());
        }
        return names;
    }
}

This should give following response:这应该给出以下响应:

{
"mycustomannotationsmarkedinterafce": [
"in.silentsudo.classpathannotationscanner.SimpleInterface"
]
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法使用 spring ClassPathScanningCandidateComponentProvider 返回所有标有特定注释的类 - Can't return all classes marked with specific annotation using spring ClassPathScanningCandidateComponentProvider 将ClassPathScanningCandidateComponentProvider与多个jar文件一起使用? - Using ClassPathScanningCandidateComponentProvider with multiple jar files? 如何查找在Spring中使用注释执行的代码? - How to find the code that is executed using an annotation in Spring? 使用ClassPathScanningCandidateComponentProvider时,Spring Boot Unittest给出NoClassDefFoundError - Spring boot unittest gives NoClassDefFoundError when using ClassPathScanningCandidateComponentProvider 如果Spring概要文件与指定的概要文件不同,如何跳过服务类,请使用注释@Profile - How to skip a Service class if the Spring profile is different than one specified, using the annotation @Profile 在@Query注释中指定查询时,如何使用Spring Data JPA初始化惰性关联? - How can I initialize lazy associations using Spring Data JPA when the query is specified in @Query annotation? 如何获取指定接口的所有bean,它也实现了其他接口 - How to get all beans of specified interface which also implements other interfaces ApplicationListener的注释版本 <ContextClosedEvent> 和类似的接口 - Annotation version of ApplicationListener<ContextClosedEvent> and similar interfaces 使用@Scheduled spring注释时出现异常(无法找到类[org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor]) - Exception when using @Scheduled spring annotation (Cannot find class [org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor]) 如何使用注释配置PayloadValidatingInterceptor - How to configure PayloadValidatingInterceptor using annotation
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM