背景
最近遇到需要在系统启动前针对自定义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;