74 lines
3.9 KiB
Java
74 lines
3.9 KiB
Java
package link.at17.mid.tushare.component;
|
||
|
||
import org.apache.commons.lang3.StringUtils;
|
||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||
import org.springframework.context.annotation.DependsOn;
|
||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||
|
||
import link.at17.mid.tushare.annotation.ConfigInfo;
|
||
import link.at17.mid.tushare.interfaces.IConfig;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
|
||
/**
|
||
* 实现自动化注册 Config
|
||
*/
|
||
@Slf4j
|
||
@DependsOn("configService")
|
||
public class ConfigAutoRegistrar implements BeanDefinitionRegistryPostProcessor {
|
||
|
||
@Override
|
||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
|
||
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
|
||
scanner.addIncludeFilter(new AssignableTypeFilter(IConfig.class));
|
||
scanner.addIncludeFilter(new AnnotationTypeFilter(ConfigInfo.class));
|
||
|
||
scanner.findCandidateComponents("link.at17.mid.tushare.system.config").forEach(beanDefinition -> {
|
||
String className = beanDefinition.getBeanClassName();
|
||
try {
|
||
// 确保其 field 规则与 configService 内 field 生成规则一致,即:
|
||
// 如果 @ConfigInfo 指定了 field 的,使用该 field + "Config"
|
||
// 作为 beanName,否则使用首字母小写的 simpleClassName 作为
|
||
// beanName,且 simpleClassName 无论如何必须以 Config 作为结尾。
|
||
Class<?> clazz = Class.forName(className);
|
||
String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase()
|
||
+ clazz.getSimpleName().substring(1);
|
||
|
||
if (!IConfig.class.isAssignableFrom(clazz)) {
|
||
log.warn("Config {} does not implement IConfig, ignore", beanName);
|
||
return;
|
||
}
|
||
|
||
if (!beanName.endsWith("Config")) {
|
||
log.warn("Config {}'s simple class name does not end with \"Config\", ignore", beanName);
|
||
return;
|
||
}
|
||
|
||
ConfigInfo info = clazz.getAnnotation(ConfigInfo.class);
|
||
if (info == null) {
|
||
log.warn("Config {} does not have @ConfigInfo annotation, ignore", clazz.getName());
|
||
return;
|
||
}
|
||
if (StringUtils.isNotBlank(info.field())) {
|
||
beanName = info.field() + "Config";
|
||
}
|
||
|
||
BeanDefinitionBuilder factoryBean = BeanDefinitionBuilder
|
||
.genericBeanDefinition(ConfigServiceFactoryBean.class)
|
||
.addConstructorArgValue(clazz);
|
||
// 注意此处注册 factoryBean 不意味着 FactoryBean.getObject() 方法立即被执行,
|
||
// Spring 管理的 Bean 默认在其被使用时才创建,所以如果 getObject() 调用一些方
|
||
// 法,这些方法会在初次使用 Bean 时才被创建。如果这些方法对于启动过程很重要,
|
||
// 需要在对应 Config(Bean) 上加上 @Bean 和 @Lazy(false) 注解,确保一旦准备好
|
||
// 相应的 Bean 就会被创建。
|
||
registry.registerBeanDefinition(beanName, factoryBean.getBeanDefinition());
|
||
log.info("Add config {} to bean register", beanName);
|
||
} catch (ClassNotFoundException e) {
|
||
throw new RuntimeException("Failed to load class: " + className, e);
|
||
}
|
||
});
|
||
}
|
||
} |