Spring自定义Annotation扫描

背景

最近遇到需要在系统启动前针对自定义Annotation的类缓存一些配置信息,以便系统快速映射调用。在翻看了一些资料后,发现spring可比较优雅的支持这种方案,其中涉及以下三个接口、类:

-ClassPathBeanDefinitionScanner - 根据包路径扫描标注实现类,也是本文改造的主体
-BeanFactoryPostProcessor - Bean加载后置处理接口
-ApplicationContextAware - 获取ApplicationContext接口

一、ClassPathBeanDefinitionScanner 继承类 AnnotationScanner

public class AnnotationScanner extends ClassPathBeanDefinitionScanner {
    /**
     * 实体类对应的AnnotationClazz
     */
    @Setter
    private Class<? extends Annotation> selfAnnotationClazz;

    /**
     * 传值使用的临时静态变量
     */
    private static Class<? extends Annotation> staticTempAnnotationClazz = null;

    /**
     * 因构造函数无法传入指定的Annotation类,需使用静态方法来调用
     * @param registry
     * @param clazz
     * @return
     */
    public static synchronized AnnotationScanner getScanner(BeanDefinitionRegistry registry,Class<? extends Annotation> clazz){
        staticTempAnnotationClazz = clazz;
        AnnotationScanner scanner = new AnnotationScanner(registry);
        scanner.setSelfAnnotationClazz(clazz);
        return scanner;
    }

    private AnnotationScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    // 构造函数需调用函数,使用静态变量annotationClazz传值
    @Override
    public void registerDefaultFilters() {
        // 添加需扫描的Annotation Class
        this.addIncludeFilter(new AnnotationTypeFilter(staticTempAnnotationClazz));
    }

    // 以下为初始化后调用的方法
    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        return super.doScan(basePackages);
    }

    @Override
    public boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return super.isCandidateComponent(beanDefinition)
                && beanDefinition.getMetadata().hasAnnotation(this.selfAnnotationClazz.getName());
    }
}

二、BeanFactoryPostProcessor 、ApplicationContextAware 接口实现类

@Component
@Lazy(true)
@Slf4j
public class TestModelAnnotationParser implements ApplicationContextAware, BeanFactoryPostProcessor {
    private static final String EVENT_NAME = "TestModel注解扫描";
    private static final String RESOURCE_PATTERN = "com.test.example";
    private static final String PATH_DOT = ".";

    private ApplicationContext applicationContext;

    /**
     * Bean加载后置处理
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 使用自定义扫描类,针对@TestModel进行扫描
        AnnotationScanner scanner = AnnotationScanner.getScanner((BeanDefinitionRegistry) beanFactory, TestModel.class);
        // 设置ApplicationContext
        scanner.setResourceLoader(this.applicationContext);
        // 执行扫描
        int count = scanner.scan(RESOURCE_PATTERN);
        log.info(EVENT_NAME + ", 扫描类数量:"+count);

        // 取得对应Annotation映射,BeanName -- 实例
        Map<String, Object> annotationMap = beanFactory.getBeansWithAnnotation(TestModel.class);

        // .... doSomething,根据需要进行设置,

    }

    /**
     * 获取ApplicationContext
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

至此,可以在需要的地方使用@Autowired,或通过ApplicationContext.getBean方式取得。当然更优雅的方式是另外配置一个Bean来提供所需内容

@Autowired
private TestModelAnnotationParser parser;

目录

展开阅读全文

没有更多推荐了,返回首页