背景
最近遇到需要在系统启动前针对自定义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);
}
@Override
public void registerDefaultFilters() {
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 {
AnnotationScanner scanner = AnnotationScanner.getScanner((BeanDefinitionRegistry) beanFactory, TestModel.class);
scanner.setResourceLoader(
this.applicationContext);
int count = scanner.scan(RESOURCE_PATTERN);
log.info(EVENT_NAME +
", 扫描类数量:"+count);
Map<String, Object> annotationMap = beanFactory.getBeansWithAnnotation(TestModel.class);
}
/**
* 获取ApplicationContext
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}
至此,可以在需要的地方使用@Autowired,或通过ApplicationContext.getBean方式取得。当然更优雅的方式是另外配置一个Bean来提供所需内容
@Autowired
private TestModelAnnotationParser parser;
目录
背景一ClassPathBeanDefinitionScanner 继承类 AnnotationScanner二BeanFactoryPostProcessor ApplicationContextAware 接口实现类目录