又名: 怎么使用Spring的Scanner去扫描加载使用了自定义注解的Bean
又名: 怎么构建一个spring-boot-starter将自定义扫描注册Bean
又名: 我没理解这个框架的设计模式
简单来说, forest 是一个很方便的使用的调用第三方URL的工具. 可以帮你从 httpClient, okHttp3的具体细节中解放出来…
同时也不至于像 openFeign 那么重量级.
非常轻便, 易于自定义扩展
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);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
// scanner.registerFilters();
if (basePackages == null || basePackages.size() == 0) {
return scanner;
}
// 最终加载bean的操作在这里;
scanner.doScan(org.springframework.util.StringUtils.toStringArray(basePackages));
return scanner;
}
那么doScan做了些什么呢
/**
* 重写扫描逻辑
* @param basePackages 请求接口类所在的包路径,只能是第一层的包,不包含子包
* @return BeanDefinitionHolder实例集合
*/
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("[Forest] No Forest client is found in package '" + Arrays.toString(basePackages) + "'.");
}
processBeanDefinitions(beanDefinitions);
return beanDefinitions;
}
调用了 super.doScan(); 查询所有能被查询到 beanDefinitions
链接: DoScan详解.
其中的
findCandidateComponents(basePackage)
是扫描目标bean的关键;
一路往下我们最终调用 scanCandidateComponents(String basePackage)
其中有一个关键的判断
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 首先判断这个路径在不在 白名单/黑名单 上.
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
// 然后再判断, 这个bean是否是 只有接口没有实现的类
// 此方法被 forest 重写了. 原本的判断条件更多;
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
}
### 注册黑名单和白名单的时间在Sacnner::New的时候, 会调用 registerFilters
/**
* 注册过滤器
*/
public void registerFilters() {
if (allInterfaces) {
// include all interfaces
addIncludeFilter((metadataReader, metadataReaderFactory) ->
interfaceFilter(metadataReader, metadataReaderFactory));
}
// exclude package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
### 至于白名单
private boolean interfaceFilter(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
if (!classMetadata.isInterface() || classMetadata.isFinal()) {
return false;
}
String[] superClassNames = metadataReader.getClassMetadata().getInterfaceNames();
boolean hasSuperForestClient = false;
for (String superClassName : superClassNames) {
try {
MetadataReader superMetaReader = metadataReaderFactory.getMetadataReader(superClassName);
hasSuperForestClient = interfaceFilter(superMetaReader, metadataReaderFactory);
} catch (IOException e) {
}
if (hasSuperForestClient) {
return true;
}
}
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
for (String annotationTypeName : annotationMetadata.getAnnotationTypes()) {
if ("com.dtflys.forest.annotation.ForestClient".equals(annotationTypeName)) {
return true;
}
if ("com.dtflys.forest.annotation.BaseRequest".equals(annotationTypeName)) {
return true;
}
}
for (String methodAnnName : FOREST_METHOD_ANNOTATION_NAMES) {
if (annotationMetadata.hasAnnotatedMethods(methodAnnName)) {
return true;
}
}
return false;
}
### 也就是判断此Class里面有没有使用了forest注解的方法, 如果有就是白名单, 会被代理;
### 黑名单, 排除java的Class
注册完成了之后执行
processBeanDefinitions(beanDefinitions);
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
ClientFactoryBeanUtils.setupClientFactoryBean(definition, configurationId, beanClassName);
logger.info("[Forest] Created Forest Client Bean with name '" + holder.getBeanName()
+ "' and Proxy of '" + beanClassName + "' client interface");
}
}
最终则是
private final static Class CLIENT_FACTORY_BEAN_CLASS = ClientFactoryBean.class;
public static void setupClientFactoryBean(AbstractBeanDefinition beanDefinition, String configurationId, String clientClassName) {
beanDefinition.setBeanClass(CLIENT_FACTORY_BEAN_CLASS);
if (configurationId != null && configurationId.length() > 0) {
beanDefinition.getPropertyValues().add("forestConfiguration", new RuntimeBeanReference(configurationId));
}
beanDefinition.getPropertyValues().add("interfaceClass", clientClassName);
}
也就是将写了forest注解的接口的beanDefintion,注册了一个对应的FactoryBean
简单来说,FactoryBean#GetObject()方法会给容器里注入Bean, bean的类型是FactoryBean#getObjectType()
在这里它的类型是传入的 clientClassName;(虽然传入的是String, 但是后续Spring的容器会将它加载为Class对象)
链接: FactoryBean的作用.
@Override
public T getObject() {
// 单例模式的典型双重检测写法 https://www.cnblogs.com/tangZH/p/10031337.html
if (forestConfiguration == null) {
synchronized (this) {
if (forestConfiguration == null) {
try {
forestConfiguration = applicationContext.getBean(ForestConfiguration.class);
} catch (Throwable th) {
}
if (forestConfiguration == null) {
forestConfiguration = ForestConfiguration.getDefaultConfiguration();
}
}
}
}
return forestConfiguration.createInstance(interfaceClass);
}
@Override
public Class<?> getObjectType() {
return interfaceClass;
}
@Override
public boolean isSingleton() {
return true;
}
也即是最终调用了 forestConfiguration.createInstance(interfaceClass);
去给需要实现的接口实现代理;
/**
* 创建请求接口的动态代理实例
*
* @param clazz 请求接口类
* @param <T> 请求接口类泛型
* @return 动态代理实例
* @see ForestConfiguration#client(Class)
*/
public <T> T createInstance(Class<T> clazz) {
ProxyFactory<T> proxyFactory = getProxyFactory(clazz);
return proxyFactory.createInstance();
}
最终就是走的代理工厂; 这是JDK的动态代理实现;
public class ProxyFactory<T> {
private ForestConfiguration configuration;
private Class<T> interfaceClass;
public ProxyFactory(ForestConfiguration configuration, Class<T> interfaceClass) {
this.configuration = configuration;
this.interfaceClass = interfaceClass;
}
public Class<T> getInterfaceClass() {
return interfaceClass;
}
public void setInterfaceClass(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}
public T createInstance() {
T instance = (T) configuration.getInstanceCache().get(interfaceClass);
boolean cacheEnabled = configuration.isCacheEnabled();
if (cacheEnabled && instance != null) {
return instance;
}
synchronized (configuration.getInstanceCache()) {
instance = (T) configuration.getInstanceCache().get(interfaceClass);
if (cacheEnabled && instance != null) {
return instance;
}
InterfaceProxyHandler<T> interfaceProxyHandler = new InterfaceProxyHandler<T>(configuration, this, interfaceClass);
instance = (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass, ForestClientProxy.class}, interfaceProxyHandler);
if (cacheEnabled) {
configuration.getInstanceCache().put(interfaceClass, instance);
}
return instance;
}
}
}
使用JDK代理, 然后将其加入缓存中;
synchronized (configuration.getInstanceCache())
锁住防止可能的并发问题;
具体代理内容就是 new InterfaceProxyHandler()
链接: Forest源码探析.
InterfaceProxyHandler
生成了代理; 然后构建ForestRequest去执行请求