从servlet到springboot(3) springboot启动 run方法解析之Banner printedBanner 为之

上一篇我们分析到

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属性.
 

猜你喜欢

转载自blog.csdn.net/m0_37139189/article/details/86230173