上一篇我们分析到
listeners.started()为止,我们接着分析
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
我们进入DefaultApplicationArguments类内部跟踪到
public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {
public SimpleCommandLinePropertySource(String... args) {
super((new SimpleCommandLineArgsParser()).parse(args));
}
有一句关键代码
new SimpleCommandLineArgsParser()).parse(args)
进入parse方法
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionName;
String optionValue = null;
if (optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf("="));
optionValue = optionText.substring(optionText.indexOf("=")+1, optionText.length());
}
else {
optionName = optionText;
}
if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
逻辑很简单,首先初始化了CommandLineArgs.然后遍历args.如果args是–开头的,就加入OptionArg中,否则加入到NonOptionArg中.
我们看下初始化完毕的
ApplicationArguments
我在启动项中加入了--spring.profiles.active=dev参数
接下去分析
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
跟入方法内部
----->
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
this.configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (this.isWebEnvironment(environment) && !this.webEnvironment) {
environment = this.convertToStandardEnvironment(environment);
}
return environment;
}
我们分析第一句话
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
------>
会根据当前环境是不是web环境来产生不同的StandardEnvironment,这里是web环境,所以产生的是StandardServletEnvironment
return (ConfigurableEnvironment)(this.webEnvironment ? new StandardServletEnvironment() :
new StandardEnvironment())
在new StandardServletEnvironment的过程中会有一些比较复杂过程我们进一步分析
看到这个类的继承关系如上图,springboot在初始化StandardServletEnviroment的时候会先初始化
AbstractEnvironment中的构造函数,而这里调用了StandardServletEnviroment中的customizePropertySources方法,同时还调用了StandardEnviroment中的customizePropertySources,代码就不贴了,主要就是在propertySources中添加一些参数
利用这个特性,我们可以自己实现propertySources,下面附上代码
public class DynamicPropertySource extends MapPropertySource {
private static Logger log = LoggerFactory.getLogger(DynamicPropertySource.class);
private static ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1);
static {
scheduled.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
map = dynamicLoadMapInfo();
}
}, 1, 10, TimeUnit.SECONDS);
}
public DynamicPropertySource(String name) {
super(name, map);
}
private static Map<String, Object> map = new ConcurrentHashMap<String, Object>(64);
@Override
public Object getProperty(String name) {
return map.get(name);
}
//动态获取资源信息
private static Map<String, Object> dynamicLoadMapInfo() {
//通过http或tcp等通信协议获取配置信息
return mockMapInfo();
}
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
private static Map<String, Object> mockMapInfo() {
Map<String, Object> map = new HashMap<String, Object>();
int randomData = new Random().nextInt();
System.out.println("random data{};currentTime:{}"+randomData+sdf.format(new Date()));
map.put("dynamic-info", randomData);
return map;
}
@Configuration
public class DynamicConfig {
public static final String DYNAMIC_CONFIG_NAME = "dynamic_config";
@Autowired
AbstractEnvironment environment;
@PostConstruct
public void init() {
environment.getPropertySources().addFirst(new DynamicPropertySource(DYNAMIC_CONFIG_NAME));
}
在启动springboot之后我们会发现控制台不断打印出信息
我们接着分析下一句话
this.configureEnvironment(environment, applicationArguments.getSourceArgs());
这句话跟进之后发现做了两件事
配置PropertySource
配置Profiles
this.configurePropertySources(environment, args);
this.configureProfiles(environment, args);
先看第一个
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
MutablePropertySources sources = environment.getPropertySources();
// 1. 如果defaultProperties不为空,则继续添加defaultProperties
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(
new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 2. 如果addCommandLineProperties为true并且有命令参数,
// 分两步骤走:第一步存在commandLineArgs则继续设置属性;第二步commandLineArgs不存在则在头部添加commandLineArgs
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource(
name + "-" + args.hashCode(), args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
此时sources中的属性有5个
我们接着分析第二句
这句话主要是设置profile的值,这里涉及到一个不同配置文件的问题,具体可以百度,这里不细说了
回到prepareEnvironment 方法中,接着下就是发布事件了
listeners.environmentPrepared(environment); 这里大部分在上一篇的listeners中分析了
我们直接看
invokeListener方法,这里第一个进入循环的Listener是ConfigFileApplicationListener
直接进入onApplicationEvent方法,可以发现是调用onApplicationEnvironmentPreparedEvent
进入候第一步同样是调用SpringFactoriesLoader.loadFactories,前面已经说过,这里获取到的postProcessors有两个
List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
Iterator var3 = postProcessors.iterator();
再把当前的listners加入,并且循环调用每个EnvironmentPostProcessor的postProcessEnvironment方法,这里不细说了,主要说下这里说执行ConfigFileApplicationListeners的postProcessEnvironment方法,主要就实现了
处理步骤如下:
1. 调用initializeActiveProfiles.获得ActiveProfiles.将未激活的Profiles加入到profiles中.如果profiles为空的话,就将spring.profiles.default配置的profile添加到profiles中.
2. 依次遍历profiles中的profile.依次在classpath:/,classpath:/config/,file:./,file:./config/中加载application的配置.调用ConfigFileApplicationListener$Loader#load进行加载.
3. 调用addConfigurationProperties,向environment中添加ConfigurationPropertySources.代码如下:
4. 如果environment不含有spring.profiles.active和spring.profiles.include的配置话,返回空集合
> 注意: 当前的environment拥有的source有
commandLineArgs、servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment, RandomValuePropertySource 如果想
不返回空的话,就需要在以上的source中有配置.最简单的方式是通过命令行的方式传入 --spring.profiles.active=you profiles 即可
5. 调用bindSpringProfiles,生成SpringProfiles
6. 调用maybeActivateProfiles.将activatedProfiles设为true
7. 初始化SpringProfiles和RelaxedDataBinder.RelaxedDataBinder读取的配置是前缀为 spring.profiles的配置.
总结一句话,就是根据--spring.profiles.active=的启动项来选择加载哪个properties中的文件
其他的lisnter暂时就不分析了,有兴趣的读者可以自行研究
本节完
2. 实例化PropertySourcesPropertyValues,调用DataBinder#bind进行数据的绑定.
3. 设置SpringProfiles的Active和Include属性.