spring boot源码学习笔记(二)

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

注明:以下内容基于spring-boot-1.4.2,starter为spring-boot-starter-web。

继实例化SpringApplication以后,变量的初始化工作已经完成,紧接着就是调用run方法完成整个启动过程。run方法包含的代码行数不多,但是包含的逻辑并不少。

    * from org.springframework.boot.SpringApplication
    /**
         * 运行spring程序,创建并且刷新一个新的ApplicationContext.
         * @param 应用参数(通常来自于java的main函数
         * @return 一个运行的ApplicationContext
         */
        public ConfigurableApplicationContext run(String... args) {
            // 步骤1:开始计时
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            FailureAnalyzers analyzers = null;
            //步骤2:配置Headless
            configureHeadlessProperty();
            //步骤3:获取活动的监听器
            SpringApplicationRunListeners listeners = getRunListeners(args);
            //步骤4:启动监听器
            listeners.started();
            try {
                //步骤5:初始化应用参数
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                //步骤6:准备运行环境
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                //步骤7:打印启动头
                Banner printedBanner = printBanner(environment);
                //步骤8:创建应用上下文ApplicationContext
                context = createApplicationContext();
                //步骤9:注册异常分析器
                analyzers = new FailureAnalyzers(context);
                //步骤10:初始化ApplicationContext
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                //步骤11:刷新ApplicationContext
                refreshContext(context);
                //步骤12:后续ApplicationContext操作
                afterRefresh(context, applicationArguments);
                //步骤13:监听器处理
                listeners.finished(context, null);
                //步骤14:结束计时并打印信息
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                return context;
            }
            catch (Throwable ex) {
                handleRunFailure(context, listeners, analyzers, ex);
                throw new IllegalStateException(ex);
            }
        }

这里涉及到一个重要的类,就是ApplicationContext,按照官方的解释这个是提供应用配置信息的核心接口,在程序运行期间它是只读的,但是如果实现支持的话可以被重新加载。这个类提供的功能包括: 1.访问应用组建的bean工程方法,继承自
org.springframework.beans.factory.ListableBeanFactory;
2.用通用的方式加载文件资源,继承自
org.springframework.core.io.ResourceLoader接口;
3.向注册的监听器发布事件,继承自
ApplicationEventPublisher接口;
4.解析消息,支持国际化,继承自
MessageSource接口;
5.context具备多级继承关系,意味这每个servlet都可以优先使用自己的派生context的功能,然后才是全局的公共的context。

下面就run方法中的每一步进行详细解析:

a. 步骤1:开始计时 这里使用到了计时工具类
org.springframework.util.StopWatch。相比直接使用
System.currentTimeMillis()获取时间戳,使用工具类的方式显得更加优雅,并且也为多次计时和多纬度计时提供了可能。在自己的代码里,也可以尝试使用这个工具类

    * from org.springframework.util.StopWatch
       /**
         * 开始一项未命名的任务。需要先调用start方法才能调用stop结束方法。
         */
        public void start() throws IllegalStateException {
            start("");
        }

        /**
         * 开始一项命名过的任务。需要先调用start方法才能调用stop结束方法。
         */
        public void start(String taskName) throws IllegalStateException {
            //已经开始了任务,不需要再次调用该方法
            if (this.running) {
                throw new IllegalStateException("Can't start StopWatch: it's already running");
            }
            this.running = true;
            this.currentTaskName = taskName;
            //记录当前时间戳
            this.startTimeMillis = System.currentTimeMillis();
        }

b.步骤2:配置Headless
配置系统变量
java.awt.hea
dless,
在该模式下,系统缺少显示设备、键盘或鼠标,依靠系统的计算能力模拟这些特性。

c.步骤3:获取活动的监听器
和初始化时获取监听器一样,这里使用getSpringFactoriesInstances方法获取SpringApplicationRunListener子类的实例,即活动监听器的实例。

    * from org.springframework.boot.SpringApplication
    private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
            return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                    SpringApplicationRunListener.class, types, this, args));
        }

同样,需要加载的活动监听器的类名在spring.factories中配置,key使用父类的声明,即
org.springframework.boot.SpringApplicationRunListener。

    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener

在获取EventPublishingRunListener实例后,再通过SpringApplicationRunListeners构造函数,将logger和实例填充到SpringApplicationRunListeners对象中。

d.步骤4:启动监听器 SpringApplicationRunListeners可以看作SpringApplicationRunListener子类实例的集合,统一对实例进行操作,比如start启动时。

    * from org.springframework.boot.SpringApplicationRunListeners
        public void started() {
            for (SpringApplicationRunListener listener : this.listeners) {
                listener.started();
            }
        }

在配置中仅包含一项活动监听器,即
EventPublishingRunListener,因此只涉及到一次调用。

    * from org.springframework.boot.context.event.EventPublishingRunListener

        //构造函数,在这里将SpringApplication中初始化过的多个ApplicationListener进行注册
        public EventPublishingRunListener(SpringApplication application, String[] args) {
            this.application = application;
            this.args = args;
            this.initialMulticaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
            }
        }

       //广播事件
        public void started() {
            this.initialMulticaster
                    .multicastEvent(new ApplicationStartedEvent(this.application, this.args));
        }

    * from org.springframework.context.event.SimpleApplicationEventMulticaster

    //广播事件的实现,向所有注册监听器广播事件,而这个时间就是传入的ApplicationStartedEvent。如果监听器未监听这个事件,那可以直接忽略。
    @Override
        public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
            ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                //如果指定executor,可以使用单独线程来触发时间,通知监听器
                Executor executor = getTaskExecutor();
                if (executor != null) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            invokeListener(listener, event);
                        }
                    });
                }
                else {
                    invokeListener(listener, event);
                }
            }
        }

这是很典型的java
监听器模式,也是观察者模式的另一种形式。每一项监听器处理的内容将在后续文章中进一步展开。到此,初始化过程中加载的监听器也派上用场了。并不是所有监听器都会处理启动事件,在各自的事件处理方法中都各有选择。

e.步骤5:初始化应用参数
这里就是将main函数带入的参数填充到
org.springframework.boot.
DefaultApplicationArguments中。

    * from org.springframework.boot.DefaultApplicationArguments
        public DefaultApplicationArguments(String[] args) {
            Assert.notNull(args, "Args must not be null");
            this.source = new Source(args);
            this.args = args;
        }

f.步骤6:准备运行环境

    * from org.springframework.boot.SpringApplication
        private ConfigurableEnvironment prepareEnvironment(
                SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
            // 创建运行环境,因初始化过程中确认了是web工程,即webEnvironment为true,因此此处创建的是StandardServletEnvironment实例。
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            // 配置运行环境,包括main函数带入的配置项和项目配置文件带入的配置项
            configureEnvironment(environment, applicationArguments.getSourceArgs());
            // 广播ApplicationEnvironmentPreparedEvent事件,通知注册的监听器做出相应处理
            listeners.environmentPrepared(environment);
            //再次确认满足web环境的要求,lib中包含了org.springframework.web.context.ConfigurableWebEnvironment。
            if (isWebEnvironment(environment) && !this.webEnvironment) {
                environment = convertToStandardEnvironment(environment);
            }
            // 环境准备结束
            return environment;
        }

g.步骤7:打印启动头

默认打印标识性头图等信息,当然如果你想个性化,也可以做到,可以自定义image头,也可以定义text头。

    * from org.springframework.boot.SpringBootBanner

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

h.步骤8:创建应用上下文ApplicationContext

web应用和非web应用分别有默认的实现类,web对应
org.springframework.
boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext,非web对应
javax.servlet.Servlet
.springframework.web.context.ConfigurableWebApplicationContext.

    * from org.springframework.boot.SpringApplication
        /**
         * 用于创建ApplicationContext的策略方法。优先考虑显示指定的上下文类,如果没有指定,则使用默认的。
         * @return 应用上下文 (还没有刷新)
         */
        protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            if (contextClass == null) {
                try {
                    contextClass = Class.forName(this.webEnvironment
                            ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
                }
                catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                            "Unable create a default ApplicationContext, "
                                    + "please specify an ApplicationContextClass",
                            ex);
                }
            }
            //实例化并返回
            return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
        }

i.步骤9:注册异常分析器
异常分析器的加载和初始化器、注册监听器相同,都是在spring.factories文件中配置,通过反射机制实例化并加载进来。

    FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
            Assert.notNull(context, "Context must not be null");
            this.classLoader = (classLoader == null ? context.getClassLoader() : classLoader);
            //加载分析器
            this.analyzers = loadFailureAnalyzers(this.classLoader);
            //将context中的属性注入到分析器中
            prepareFailureAnalyzers(this.analyzers, context);
        }

        private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {
            // 获取配置文件中的配置项,key是org.springframework.boot.diagnostics.FailureAnalyzer
            List<String> analyzerNames = SpringFactoriesLoader
                    .loadFactoryNames(FailureAnalyzer.class, classLoader);
            List<FailureAnalyzer> analyzers = new ArrayList<FailureAnalyzer>();
            for (String analyzerName : analyzerNames) {
                try {
                    // 实例化
                    Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader)
                            .getDeclaredConstructor();
                    ReflectionUtils.makeAccessible(constructor);
                    analyzers.add((FailureAnalyzer) constructor.newInstance());
                }
                catch (Throwable ex) {
                    log.trace("Failed to load " + analyzerName, ex);
                }
            }
            // 排序
            AnnotationAwareOrderComparator.sort(analyzers);
            return analyzers;
        }

j.步骤10:初始化ApplicationContext
后续步骤10-步骤12都是对应用上下文进行操作,从准备,刷新到后续处理,过程很长,需要详细描述。

k.步骤13:监听器处理
结束监听器监听

l.步骤14:结束计时并打印信息
计时结束并打印时间信息。


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot源码学习笔记(二)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏