spring boot 源码探索(三)

4、ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

listeners.starting()执行完后,接着执行new DefaultApplicationArguments对象。该对象对args参数封装,创建一个PropertySource,来存放args参数。

public DefaultApplicationArguments(String[] args) {
        Assert.notNull(args, "Args must not be null");
        this.source = new Source(args);
        this.args = args;
    }
private static class Source extends SimpleCommandLinePropertySource {

        Source(String[] args) {
            super(args);
        }

    }
Source继承SimpleCommandLinePropertySource、SimpleCommandLinePropertySource最后继承PropertySource抽象类。

4、ConfigurableEnvironment environment = prepareEnvironment(listeners,  applicationArguments);

environment 代表程序的运行环境,这里prepareEvironment方法是准备运行环境,listeners 和args 参数传进去。prepareEvironment内容:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
       
        ConfigurableEnvironment environment = getOrCreateEnvironment(); //创建environment对象
        configureEnvironment(environment, applicationArguments.getSourceArgs()); //配置运行环境参数
        listeners.environmentPrepared(environment); //广播监听
        
if (isWebEnvironment(environment) && !this.webEnvironment) { environment = convertToStandardEnvironment(environment); } return environment; }

首先调用getOrCreateEnvironment 创建一个运行环境environment。根据webEnvironment 判定,创建enviroment类型,这里在initialize的时候webEnvironment 已经是true。所以创建了一个standardServletEnvironment 的environment对象。

private ConfigurableEnvironment getOrCreateEnvironment() {
   if (this.environment != null) {
      return this.environment;
   }
   if (this.webEnvironment) {
      return new StandardServletEnvironment();
   }
   return new StandardEnvironment();
}

 接着进行config Environment 环境,configureEnvironment方法主要做两件事、configurePropertySources 和 configureProfiles 处理,处理propertySource类型和profiles类型的配置处理。

protected void configureEnvironment(ConfigurableEnvironment environment,
            String[] args) {
        configurePropertySources(environment, args);
        configureProfiles(environment, args);
    }

protected
void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { sources.addLast( new MapPropertySource("defaultProperties", this.defaultProperties)); } 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)); } } } protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { environment.getActiveProfiles(); // ensure they are initialized // But these ones should go first (last wins in a property key clash) Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles); profiles.addAll(Arrays.asList(environment.getActiveProfiles())); environment.setActiveProfiles(profiles.toArray(new String[profiles.size()])); }

在configureProertySources中,首先查看defaultProperties是否为空,不为null,则添加该Properties进environment 环境中,然后根据addCommandLineProperties和args参数是否大于0来判断是添加还是替换source。args参数为0,则添加一个由args参数组成的ProertySources,添加到environment 中sources的第一位置(addFirst添加到第一位置,确保main方法中的配置是最优先的,且可以覆盖其他地方的配置)。在本方法中,defaultProperties默认为空,args参数个数未大于0.所以这个方法什么都没有做。

在configureProfiles 中,首先先获key 为spring.profiles.active 的配置项,添加到environment中,然后添加additionalProfiles到配置项profiles中。spring.profiles.active 未配置,additionalProfiles是空对象,所以这个方法实际上也是没有做具体操作。至此,configureEnvironment 方法操作完成,接着调用监听器广播器,发布一个环境准备事件listeners.environmentPrepared(environment),事件发布跟前面发布的start事件过程是一样的,这里发布了一个ApplicationEnvironmentPreparedEvent事件。

// 发布事件
public
void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( this.application, this.args, environment)); }

下面看看监听器响应事件内容、看看多少个监听器响应了该事件,并做了什么操作:首先ClearCachesApplicationListener和ParentContextCloserApplicationListener未监听。

FileEncodingApplicationListener 监听器监听了该事件,监听代码执行内容为:

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
                event.getEnvironment(), "spring.");
        if (resolver.containsProperty("mandatoryFileEncoding")) {
            String encoding = System.getProperty("file.encoding");
            String desired = resolver.getProperty("mandatoryFileEncoding");
            if (encoding != null && !desired.equalsIgnoreCase(encoding)) {
                logger.error("System property 'file.encoding' is currently '" + encoding
                        + "'. It should be '" + desired
                        + "' (as defined in 'spring.mandatoryFileEncoding').");
                logger.error("Environment variable LANG is '" + System.getenv("LANG")
                        + "'. You could use a locale setting that matches encoding='"
                        + desired + "'.");
                logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL")
                        + "'. You could use a locale setting that matches encoding='"
                        + desired + "'.");
                throw new IllegalStateException(
                        "The Java Virtual Machine has not been configured to use the "
                                + "desired default character encoding (" + desired
                                + ").");
            }
        }
    }

看得出是获取系统的属性mandatoryFileEncoding 和 file.encoding属性,判定是否一致,这里没有配置mandatoryFileEncoding,所有这里的方法,if条件内容就不执行,该监听器没有做任何事情。

AnsiOutputApplicationListener 监听器监听该事件,代码内容:

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
                event.getEnvironment(), "spring.output.ansi.");
        if (resolver.containsProperty("enabled")) {
            String enabled = resolver.getProperty("enabled");
            AnsiOutput.setEnabled(Enum.valueOf(Enabled.class, enabled.toUpperCase()));
        }

        if (resolver.containsProperty("console-available")) {
            AnsiOutput.setConsoleAvailable(
                    resolver.getProperty("console-available", Boolean.class));
        }
    }

也是通过属性解析器,获取到spring.output.ansi和console-available属性,然后对AnsiOutput类进行设置。(AnsiOutput类的解释里是 “生成ANSI编码的输出。。。。”,没搞懂)这里没有配置那两个属性,所以这个监听器也是没有做任何操作。

ConfigFileApplicationListener监听器监听了事件。

public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent(
                    (ApplicationEnvironmentPreparedEvent) event);
        }
        if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent(event);
        }
    }

    private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        postProcessors.add(this);
        AnnotationAwareOrderComparator.sort(postProcessors);
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessEnvironment(event.getEnvironment(),
                    event.getSpringApplication());
        }
    }

    List<EnvironmentPostProcessor> loadPostProcessors() {
        return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,
                getClass().getClassLoader());
    }

可以看出,该监听器通过loadFactories加载spring.factories中的两个processor处理器,分别是CloudFoundryVcapEnvironmentPostProcessor和SpringApplicationJsonEnvironmentPostProcessor处理器。然后执行两个处理器的postProcessEnvironment方法,用于处理配置文件配置CloudFoundry和Json相关信息。这里没有做任何相关配置,所以这两个处理器未处理任何事情。

LoggingApplicationListener 监听器监听该事件,用于初始化日志工作、在ApplicationStart事件中,已经做了预初始化,这里做初始化,默认选用的是logback日志

public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent) {
            onApplicationStartingEvent((ApplicationStartingEvent) event);
        }
        else if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent(
                    (ApplicationEnvironmentPreparedEvent) event);
        }
        else if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent((ApplicationPreparedEvent) event);
        }
        else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
                .getApplicationContext().getParent() == null) {
            onContextClosedEvent();
        }
        else if (event instanceof ApplicationFailedEvent) {
            onApplicationFailedEvent();
        }
    }
private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        if (this.loggingSystem == null) {
            this.loggingSystem = LoggingSystem
                    .get(event.getSpringApplication().getClassLoader());
        }
        initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
    }
protected void initialize(ConfigurableEnvironment environment,
            ClassLoader classLoader) {
        new LoggingSystemProperties(environment).apply();
        LogFile logFile = LogFile.get(environment);
        if (logFile != null) {
            logFile.applyToSystemProperties();
        }
        initializeEarlyLoggingLevel(environment);
        initializeSystem(environment, this.loggingSystem, logFile);
        initializeFinalLoggingLevels(environment, this.loggingSystem);
        registerShutdownHookIfNecessary(environment, this.loggingSystem);
    }

到这里,监听器都监听并执行监听操作完成了,接着判定一下是否是web environment 环境,不是,就创建标准的StandardEnvironment 环境返回。

if (isWebEnvironment(environment) && !this.webEnvironment) {
            environment = convertToStandardEnvironment(environment);
        }
private ConfigurableEnvironment convertToStandardEnvironment(
            ConfigurableEnvironment environment) {
        StandardEnvironment result = new StandardEnvironment();
        removeAllPropertySources(result.getPropertySources());
        result.setActiveProfiles(environment.getActiveProfiles());
        for (PropertySource<?> propertySource : environment.getPropertySources()) {
            if (!SERVLET_ENVIRONMENT_SOURCE_NAMES.contains(propertySource.getName())) {
                result.getPropertySources().addLast(propertySource);
            }
        }
        return result;
    }

这样 prepareEnvironment()方法就执行完成了,然后返回ConfigurableEnvironment 对象environment。接着执行Banner 输出。并执行的图标显示。

5、Banner printedBanner = printBanner(environment);

printBanner 方法在应用程序启动时候打印一个图案,并输出一些版本信息。

private Banner printBanner(ConfigurableEnvironment environment) {
        if (this.bannerMode == Banner.Mode.OFF) {
            return null;
        }
        ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
                : new DefaultResourceLoader(getClassLoader());
        SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
                resourceLoader, this.banner);
        if (this.bannerMode == Mode.LOG) {
            return bannerPrinter.print(environment, this.mainApplicationClass, logger);
        }
        return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
    }

从代码看首选判定是否开启banner显示,若为off,将返回空值,不打印信息。然后创建一个bannerPrinter 打印对象。调用该对象的print方法打印banner图案信息。如果bannerMode是Log则通过logger打印一句信息,否则则通过system.out 打印

public Banner print(Environment environment, Class<?> sourceClass, Log logger) {
        Banner banner = getBanner(environment, this.fallbackBanner);
        try {
            logger.info(createStringFromBanner(banner, environment, sourceClass));
        }
        catch (UnsupportedEncodingException ex) {
            logger.warn("Failed to create String for banner", ex);
        }
        return new PrintedBanner(banner, sourceClass);
    }
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
        Banner banner = getBanner(environment, this.fallbackBanner);
        banner.printBanner(environment, sourceClass, out);
        return new PrintedBanner(banner, sourceClass);
    }

private static final Banner DEFAULT_BANNER = new SpringBootBanner();


private
Banner getBanner(Environment environment, Banner definedBanner) { Banners banners = new Banners(); banners.addIfNotNull(getImageBanner(environment)); banners.addIfNotNull(getTextBanner(environment)); if (banners.hasAtLeastOneBanner()) { return banners; } if (this.fallbackBanner != null) { return this.fallbackBanner; } return DEFAULT_BANNER; }

logger和system.out方式都是通过getBanner方法获取到具体的banner对象,然后进行打印。这里没有配置任何banner,所以使用了默认的banner(SpringBootBanner)。

SpringBootBanner代码如下,从代码就可以看到我们的打印图标了

class SpringBootBanner implements Banner {

    private static final String[] BANNER = { "",
            "  .   ____          _            __ _ _",
            " /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\",
            "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
            " \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )",
            "  '  |____| .__|_| |_|_| |_\\__, | / / / /",
            " =========|_|==============|___/=/_/_/_/" };

    private static final String SPRING_BOOT = " :: Spring Boot :: ";

    private static final int STRAP_LINE_SIZE = 42;

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass,
            PrintStream printStream) {
        for (String line : BANNER) {
            printStream.println(line);
        }
        String version = SpringBootVersion.getVersion();
        version = (version == null ? "" : " (v" + version + ")");
        String padding = "";
        while (padding.length() < STRAP_LINE_SIZE
                - (version.length() + SPRING_BOOT.length())) {
            padding += " ";
        }

        printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT,
                AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version));
        printStream.println();
    }

}

printBanner方法将图标和springboot 名字和版本信息格式化输出。程序启动后即可以看到如下画面

 

 到这里,程序启动输出的第一个信息出来了。。。接下来的方法就是创建Application应用上下文具体内容了。。。。

=================================================================================================================================

 

posted @ 2020-04-26 17:39  WRS+  阅读(207)  评论(0)    收藏  举报