在项目开发中,我们可能会有这样的需求,通过注解配合AOP来实现一些业务额外功能,比如鉴权、日志记录等,更甚至我们可能会想通过注解,就可以把bean托管给spring管理,比如通过@service注解等。今天我们就来演示如何通过扫描自定义注解,就可以把bean托管给spring。
本示例是通过自定义注解@BingLogService来实现一个既能把bean托管给spring管理,又能实现日志记录的功能。从前言的描述,我们可以知道通过自定义注解把bean托管给spring管理,主要分为两步,第一步:扫描,第二步 注册bean到spring
1、扫描自定义注解
通过定义一个ImportBeanDefinitionRegistrar的实现类,在实现类中可以使用ClassPathBeanDefinitionScanner进行扫描并自动注册,它是ClassPathScanningCandidateComponentProvider的子类,所以还是可以添加相同的TypeFilter,然后通过scanner.scan(basePackages)扫描指定的basePackage下满足条件的Class并注册它们为bean。
public class BindScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
private ResourceLoader resourceLoader;
private ClassLoader classLoader;
private Environment environment;
public BindScannerRegistrar() { }
@Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; }
@Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; }
@Override public void setEnvironment(Environment environment) { this.environment = environment; }
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> basePackages = getBasePackages(importingClassMetadata); ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry); scanner.setResourceLoader(this.resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(BingLogService.class));
}
}
2、注册bean到spring
借助BeanDefinition来创建Bean定义并注册到BeanFactory中,BeanDefinition要如何获取?在ImportBeanDefinitionRegistrar的实现类或者BeanDefinitionRegistryPostProcessor都可以找到BeanDefinition。具体注册代码核心代码如下
private void registerLogService(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) { try { String className = annotationMetadata.getClassName(); Class<?> beanClazz = Class.forName(className); if (!beanClazz.isAnnotationPresent(BingLogService.class)) { throw new RuntimeException("BingLogService is required!"); } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz); GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition(); definition.getPropertyValues().add("logClz", beanClazz); definition.setBeanClass(LogServiceFactroyBean.class); definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE); String beanId = org.apache.commons.lang3.StringUtils.uncapitalize(beanClazz.getSimpleName()); registry.registerBeanDefinition(beanId, definition);
} catch (ClassNotFoundException e) { log.error( "Could not register target class: " + annotationMetadata.getClassName(), e); } }
通过上面的步骤就可以实现完成75%带有自动注入到spring的注解,为什么是75%呢,因为ImportBeanDefinitionRegistrar只能通过由其它类import的方式来加载,通常是主启动类或者注解,比如再写一个@EnableBindLog,其代码如下:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(value= BindScannerRegistrar.class)public @interface EnableBindLog { String[] basePackages() default {}; Class<?>[] basePackageClasses() default {};}
在启动类上加上@EnableBindLog注解
Spring(33)——ImportBeanDefinitionRegistrar介绍
https://elim.iteye.com/blog/2430132
SpringBoot基础篇Bean之动态注册
https://blog.csdn.net/liuyueyi25/article/details/83244255
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-scan-annotation