从Nacos看Spring环境初始化
首先,在Spring的run函数中
在实际的初始化及装载Bean之前,会进行初始化环境,方便Bean的初始化
对于Enviroment的初始化在SpringApplication#prepareEnviroment中
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = this.getOrCreateEnvironment(); this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach((Environment)environment); listeners.environmentPrepared((ConfigurableEnvironment)environment); this.bindToSpringApplication((ConfigurableEnvironment)environment); if (!this.isCustomEnvironment) { environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass()); } ConfigurationPropertySources.attach((Environment)environment); return (ConfigurableEnvironment)environment; } |
在上述代码中,初始化了环境,
进行了环境的装载
主要入口在listeners中
在Listeners中,一路跟随onApplicationEvent进入到ConfigFileApplicationListener之中
在其中判断了是否是环境准备Event,然后执行环境初始化
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) { this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event); } if (event instanceof ApplicationPreparedEvent) { this.onApplicationPreparedEvent(event); } } |
在其中装载了Processor,然后排序后进行依次执行
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors(); postProcessors.add(this); AnnotationAwareOrderComparator.sort(postProcessors); Iterator var3 = postProcessors.iterator(); while(var3.hasNext()) { EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next(); postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } } |
其中的loadPostProcessors()负责装载了处理器,传入了interface org.springframework.boot.env.EnvironmentPostProcessor来扫描反射获取类并实例化
这就引入出了我们第一个主角
EnvironmentPostProcessor接口了,其中提供了一个接口
public interface EnvironmentPostProcessor {
void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application); } |
负责处理环境变量
我们拿一个CloudFoundryProcessor来举例
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) { Properties properties = new Properties(); JsonParser jsonParser = JsonParserFactory.getJsonParser(); this.addWithPrefix(properties, this.getPropertiesFromApplication(environment, jsonParser), “vcap.application.”); this.addWithPrefix(properties, this.getPropertiesFromServices(environment, jsonParser), “vcap.services.”); MutablePropertySources propertySources = environment.getPropertySources(); if (propertySources.contains(“commandLineArgs”)) { propertySources.addAfter(“commandLineArgs”, new PropertiesPropertySource(“vcap”, properties)); } else { propertySources.addFirst(new PropertiesPropertySource(“vcap”, properties)); } } } |
在内部的操作并不困难,只需要根据自己的需求,往Enviroment中的PropertySources添加PropertySource接口的实现类即可
整体实现后,别忘在spring.factories进行生命此为配置处理器
然后就是Nacos实现
其实现了PropertySourceLocator接口
NacosPropertySourceLocator#locate接口
这个接口流程则是在实现了ApplicationContextIntializer中的PropertySourceBootstrapConfiguration中进行了初始化
获取到了实现了PropertySourceLocator接口的类,然后在其中循环遍历处理
PropertySourceLocator locator = (PropertySourceLocator)var5.next();
source = locator.locateCollection(environment); |
在其中调用了NacosPropertySourceLocator
Nacos的内部实现为
首先装配了Nacos客户端
this.nacosConfigProperties.setEnvironment(env);
ConfigService configService = this.nacosConfigManager.getConfigService(); |
然后尝试去获取配置
long timeout = (long)this.nacosConfigProperties.getTimeout();
this.nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout); String name = this.nacosConfigProperties.getName(); String dataIdPrefix = this.nacosConfigProperties.getPrefix(); if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = name; } if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = env.getProperty(“spring.application.name”); } CompositePropertySource composite = new CompositePropertySource(“NACOS”); this.loadSharedConfiguration(composite); this.loadExtConfiguration(composite); this.loadApplicationConfiguration(composite, dataIdPrefix, this.nacosConfigProperties, env); return composite; |
这里返回composite,就是Nacos自己实现的PropertySource
方便Spring获取
里面维护了一个Map集合
Map里面是不同yml文件的数据
那么,Nacos的配置文件已经完成了
我们尝试实现一个自己的PropertySourceLocator
@Order(1)
public class MyPropertySourceLoader implements PropertySourceLocator { @Override public PropertySource<?> locate(Environment environment) { StandardServletEnvironment myEnv = (StandardServletEnvironment) environment; MutablePropertySources propertySources = myEnv.getPropertySources(); CompositePropertySource composite = new CompositePropertySource(“My”); MyPropertySource myPropertySource = new MyPropertySource(“testProp”, “MyTest”); composite.addPropertySource(myPropertySource); return composite; } } |
实现的PropertySource如下
class MyPropertySource<T> extends PropertySource {
private Map<String, T> propMap = new HashMap<>(); public MyPropertySource(String name, T source) { super(name, source); this.propMap.put(name, source); } @Override public T getProperty(String name) { return propMap.get(name); } } |
这样就实现了一个基本的数据装配类
我们可以自定义从一些特殊地方获取配置,比如阿里云的密码管理 AWS的SerectManager