0x00:Forest源码分析(1)
又名: 怎么使用Spring的Scanner去扫描加载使用了自定义注解的Bean
又名: 怎么构建一个spring-boot-starter将自定义扫描注册Bean
又名: 我没理解这个框架的设计模式
0x01:简单使用
- 导入 starter, 然后注解, 结束
- 这里是官方文档: 中文文档.
简单来说, forest 是一个很方便的使用的调用第三方URL的工具. 可以帮你从 httpClient, okHttp3的具体细节中解放出来…
同时也不至于像 openFeign 那么重量级.
非常轻便, 易于自定义扩展
0x02:当你导入starter的时候, 它干了些啥
resources/META-INF/spring.factories
引入了 com.dtflys.forest.springboot.ForestAutoConfiguration
为什么起作用, 在启动spring-boot时 @SpringBootApplication->@EnableAutoConfiguration
会扫描所有依赖的 resources/META-INF/spring.factories
将加载对应的文件中写的bean;
参考此篇 如何激活自动装配
接下来就会发现此类会被装配; (只粘贴关键代码)
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ComponentScan("com.dtflys.forest.springboot.properties")
@EnableConfigurationProperties({
ForestConfigurationProperties.class})
@Import({
ForestScannerRegister.class})
public class ForestAutoConfiguration {
@Autowired(required = false)
private ConfigurableApplicationContext applicationContext;
@Bean
@ConditionalOnMissingBean
public ForestBeanRegister getForestBeanRegister(ForestConfigurationProperties forestConfigurationProperties) {
ForestBeanRegister register = new ForestBeanRegister(applicationContext, forestConfigurationProperties);
register.registerForestConfiguration(forestConfigurationProperties);
register.registerScanner(forestConfigurationProperties);
return register;
}
}
1, 首先@Import({ForestScannerRegister.class})
此类会将所有准备好所有需要扫描的包名 List<String> basePackages
public class ForestScannerRegister implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
... 略
// 若 @ForestScan 注解未定义扫描包名,则扫描整个项目
if (basePackages.isEmpty()) {
basePackages.addAll(AutoConfigurationPackages.get(beanFactory));
}
... 略
}
public class ForestScannerRegister implements BeanFactoryAware, ImportBeanDefinitionRegistrar
实现了ImportBeanDefinitionRegistrar;
方法 void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
第一个参数是元注解;指的是 ` ForestAutoConfiguration ` 调用者的注解信息;
第二个参数是bean定义的注册工厂;
需要关注的是:这个能获取到Spring容器启动会扫描的所有的路径; beanFactory 是 spring的BeanFactory
如果之前写了扫描的基础路径, 就能节省很多扫描的时间;
2, 然后开始往容器里注入bean; 都是对基础配置属性的加载, 还有默认值的设置, 略
3, register.registerScanner(forestConfigurationProperties);
public ClassPathClientScanner registerScanner(ForestConfigurationProperties forestConfigurationProperties) {
// 之前在 @Import({ForestScannerRegister.class}) 里注册的所有 路径
List<String> basePackages = ForestScannerRegister.getBasePackages();
String configurationId = ForestScannerRegister.getConfigurationId();
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
// 这是自定义的 beanScanner
ClassPathClientScanner scanner = new ClassPathClientScanner(configurationId, registry