1. 前言
我们对Mybatis如何将Mapper接口注入Spring IoC进行了分析,有同学问这个有什么用,这个作用其实挺大的,比如让你实现一个类似@Controller
的注解(或者继承某个统一接口)来完成比如定时任务的统一注入或者Websocket处理器的统一注入等这种将某种共性的Bean动态注入。
// 模仿 Controller@XBean(description = "ETL JOB")public class JobShedule {@Caller(cron = "* * 0/5 * * ?")public void exec(){// job }}
以上伪代码就是一个模仿Controller的定时任务Bean。
2. 设计思路
详细的开发设计思路我已经总结好了,各位同学只要按部就班就可以实现这个功能了。
2.1 定义扫描注解
定义一个类似@MappScan
的进行导入自定义ImportBeanDefinitionRegistrar
,并指定扫描包范围。
@Documented@Inherited@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Import(XBeanDefinitionRegistrar.class)public @interface XBeanScan {String[] basePackages();}
我们自定义了一个扫描注解@XBeanScan
。它有两个作用:
- 通过
basePackages
指定扫描包的范围。 - 导入我们自定义
ImportBeanDefinitionRegistrar
的实现XBeanDefinitionRegistrar
。
2.2 定义目标Bean的通用标记
通常我们可以选择一个标识接口,所有其实现类都会注入Spring IoC;或者用更加方便的注解,所有被该注解标记的类都将注入Spring IoC。这里我们使用更加灵活方便的注解,实现了一个@XBean
标记注解:
@Documented@Inherited@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})public @interface XBean {String description() default "";}
2.3 实现扫描器
Spring框架为我们提供了扫描器来注册被标记的Bean,我们继承它进行稍加改造:
public class XBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {public XBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {super(registry, useDefaultFilters);super.addIncludeFilter(new AnnotationTypeFilter(XBean.class));}}
这里我们不使用默认的过滤器,我们指定了扫描器扫描的目标为被@XBean
标记的那些Bean。
2.4 实现 Bean 注册机
重头戏来了,我们需要将2.1到2.3定义的这些组件在ImportBeanDefinitionRegistrar
的实现中组装起来。
/** * The type X bean definition registrar. */public class XBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {private ResourceLoader resourceLoader;@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 不使用默认过滤器XBeanDefinitionScanner xBeanDefinitionScanner = new XBeanDefinitionScanner(registry, false);xBeanDefinitionScanner.setResourceLoader(resourceLoader);// 扫描XBeanScan注解指定的包xBeanDefinitionScanner.scan(getBasePackagesToScan(importingClassMetadata));}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}/** * 获取{@link XBeanScan}中声明的扫描包路径 * @param metadata the meta * @return包路径数组 */private String[] getBasePackagesToScan(AnnotationMetadata metadata) {String name = XBeanScan.class.getName();AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()+ " annotated with " + ClassUtils.getShortName(name) + "?");return attributes.getStringArray("basePackages");}}
从注解元数据importingClassMetadata
解析我们需要的扫描路径basePackages
等元数据,然后让扫描器在该路径扫描即可。
2.5 使用
在具有@Configuration
标记的类或者Spring Boot的Main
类上使用@XBeanScan
即可,是不是非常简单!
其实
@ComponentScan
提供类似的功能。
3. 总结
如果你需要更加细粒度控制就加上那些BeanDefinitionRegistryPostProcessor
和FactoryBean
等Spring提供的功能性接口。
在此我向大家推荐一个架构学习交流圈。交流学习微信:539413949(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多