美文网首页SpringSpring boot@IT·互联网
Spring Boot学习笔记03--深入了解SpringBoo

Spring Boot学习笔记03--深入了解SpringBoo

作者: 飘逸峰 | 来源:发表于2016-12-20 12:37 被阅读15040次

    摘要

    看完本文你将掌握如下知识点:

    1. SpringApplication的作用及运行过程
    2. SpringBootServletInitializer的作用及运行过程

    PS:本节内容略显枯燥,如果对SpringBoot的启动过程不感兴趣,可以略过。


    SpringBoot系列Spring Boot学习笔记


    深入了解SpringApplication

    @SpringBootApplication
    public class SpringBootWebDemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(SpringBootWebDemoApplication.class, args);
        }
    }
    

    这就是SpringBoot的启动入口,通过前面的学习我们大体上了解了@SpringBootApplication的作用,接下来我们来认识一下SpringApplication
    SpringApplication (Spring Boot Docs 1.4.2.RELEASE API)

    SpringApplication.run(SpringBootWebDemoApplication.class, args);

    通过源码我们来看一下SpringApplication.run()方法的执行过程
    1.调用static方法

    //1
    public static ConfigurableApplicationContext run(Object source, String... args) {return run(new Object[]{source}, args);}
    public static ConfigurableApplicationContext run(Object[] sources, String[] args) 
    {return (new SpringApplication(sources)).run(args);}
    

    2.创建SpringApplication对象

    //2
    public SpringApplication(Object... sources) {
            this.bannerMode = Mode.CONSOLE; //banner的打印模式,此时是控制台模式
            this.logStartupInfo = true; //开启日志
            this.addCommandLineProperties = true;//启用CommandLineProperties
            this.headless = true;//开启headless模式支持
            this.registerShutdownHook = true;//启用注册ShutdownHook,用于在非Web应用中关闭IoC容器和资源
            this.additionalProfiles = new HashSet();
            this.initialize(sources);//初始化
        }
    

    PS:Headless参考资料:在 Java SE 平台上使用 Headless 模式

    3.初始化相关对象和属性

    //3
    private void initialize(Object[] sources) {
            if(sources != null && sources.length > 0) {
                this.sources.addAll(Arrays.asList(sources));
            }
            //3.1判断是否是web运行环境,如果classpath中是否含有**WEB_ENVIRONMENT_CLASSES**指定的全部类,则返回true
            this.webEnvironment = this.deduceWebEnvironment();
            //3.2找到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化
            this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
            //3.3找到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化
            this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
            //3.4获得当前执行main方法的类对象,这里就是SpringBootWebDemoApplication的实例
            this.mainApplicationClass = this.deduceMainApplicationClass();
        }
    

    3.1 判断是否是web运行环境
    如果classpath中是否含有WEB_ENVIRONMENT_CLASSES指定的全部类,则返回true,用于创建指定类型的ApplicationContext对象。

    //3.1
    private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
    

    3.2 大体的过程就是通过SpringFactoriesLoader检索META-INF/spring.factories,找到声明的所有ApplicationContextInitializer的实现类并将其实例化。
    ApplicationContextInitializer是Spring框架中的接口,其作用可以理解为在ApplicationContext执行refresh之前,调用ApplicationContextInitializer的initialize()方法,对ApplicationContext做进一步的设置和处理。

    public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
        void initialize(C var1);
    }
    

    spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationContextInitializer

    # Application Context Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
    org.springframework.boot.context.ContextIdApplicationContextInitializer,\
    org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
    org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
    

    spring-boot-autoconfigure-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationContextInitializer

    # Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
    

    3.3 大体的过程就是通过SpringFactoriesLoader检索META-INF/spring.factories,找到声明的所有ApplicationListener的实现类并将其实例化。
    ApplicationListener是Spring框架中的接口,就是事件监听器,其作用可以理解为在SpringApplicationRunListener发布通知事件时,由ApplicationListener负责接收。

    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
        void onApplicationEvent(E var1);
    }
    

    SpringBoot只提供了一个SpringApplicationRunListener的实现类,就是EventPublishingRunListener,起作用就是在SpringBoot启动过程中,负责注册ApplicationListener监听器,在不同的时点发布不同的事件类型,如果有哪些ApplicationListener的实现类监听了这些事件,则可以接收并处理。

    public interface SpringApplicationRunListener {
        //通知监听器,SpringBoot开始执行
        void started();
        //通知监听器,Environment准备完成
        void environmentPrepared(ConfigurableEnvironment var1);
        //通知监听器,ApplicationContext已经创建并初始化完成
        void contextPrepared(ConfigurableApplicationContext var1);
        //通知监听器,ApplicationContext已经完成IoC配置加载
        void contextLoaded(ConfigurableApplicationContext var1);
        //通知监听器,SpringBoot启动完成
        void finished(ConfigurableApplicationContext var1, Throwable var2);
    }
    

    spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationListener

    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.ClearCachesApplicationListener,\
    org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    org.springframework.boot.context.FileEncodingApplicationListener,\
    org.springframework.boot.context.config.AnsiOutputApplicationListener,\
    org.springframework.boot.context.config.ConfigFileApplicationListener,\
    org.springframework.boot.context.config.DelegatingApplicationListener,\
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
    org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
    org.springframework.boot.logging.LoggingApplicationListener
    

    spring-boot-autoconfigure-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationListener

    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.autoconfigure.BackgroundPreinitializer
    

    spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的SpringApplicationRunListener

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

    3.4 获得当前执行main方法的类对象,这里就是SpringBootWebDemoApplication的实例。

    4.核心方法

    //4
    public ConfigurableApplicationContext run(String... args) {
            //开启任务执行时间监听器
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
    
            ConfigurableApplicationContext context = null;
            Object analyzers = null;
    
            //设置系统属性『java.awt.headless』,为true则启用headless模式支持
            this.configureHeadlessProperty();
    
            //通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
            //找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
            //之后逐个调用其started()方法,广播SpringBoot要开始执行了。
            SpringApplicationRunListeners listeners = this.getRunListeners(args);
            listeners.started();
    
            try {
                DefaultApplicationArguments ex = new DefaultApplicationArguments(args);
    
                //创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
                //并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
                ConfigurableEnvironment environment = this.prepareEnvironment(listeners, ex);
    
                //决定是否打印Banner
                Banner printedBanner = this.printBanner(environment);
    
                //根据webEnvironment的值来决定创建何种类型的ApplicationContext对象
                //如果是web环境,则创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
                //否则创建org.springframework.context.annotation.AnnotationConfigApplicationContext
                context = this.createApplicationContext();
    
                //注册异常分析器
                new FailureAnalyzers(context);
    
                //为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
                //并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
                //之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
                //这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
                this.prepareContext(context, environment, listeners, ex, printedBanner);
    
                //初始化所有自动配置类,调用ApplicationContext的refresh()方法
                this.refreshContext(context);
    
                //遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
                //该过程可以理解为是SpringBoot完成ApplicationContext初始化前的最后一步工作,
                //我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
                this.afterRefresh(context, ex);
    
                //调用所有的SpringApplicationRunListener的finished()方法,广播SpringBoot已经完成了ApplicationContext初始化的全部过程。
                listeners.finished(context, (Throwable)null);
    
                //关闭任务执行时间监听器
                stopWatch.stop();
                //如果开启日志,则答应执行是时间
                if(this.logStartupInfo) {
                    (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
                }
    
                return context;
            } catch (Throwable var9) {
                //调用异常分析器打印报告,调用所有的SpringApplicationRunListener的finished()方法将异常信息发布出去
                this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
                throw new IllegalStateException(var9);
            }
        }
    

    spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的FailureAnalyzerFailureAnalysisReporters

    # Failure Analyzers
    org.springframework.boot.diagnostics.FailureAnalyzer=\
    org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer
    
    # FailureAnalysisReporters
    org.springframework.boot.diagnostics.FailureAnalysisReporter=\
    org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
    

    说明

    • SpringBoot的启动过程,实际上就是对ApplicationContext的初始化过程。
    • ApplicationContext创建后立刻为其设置Environmen,并由ApplicationContextInitializer对其进一步封装。
    • 通过SpringApplicationRunListener在ApplicationContext初始化过程中各个时点发布各种广播事件,并由ApplicationListener负责接收广播事件。
    • 初始化过程中完成IoC的注入,包括通过@EnableAutoConfiguration导入的各种自动配置类。
    • 初始化完成前调用ApplicationRunner和CommandLineRunner的实现类。

    扩展SpringApplication

    通过上面的学习,我们基本上了解了,如果要对SpringApplication进行扩展,我们可以选择如下三种方案:

    • 创建ApplicationContextInitializer的实现类
    • 创建ApplicationListener的实现类
    • 创建ApplicationRunner和CommandLineRunner的实现类

    1.可以通过如下方式加载自定义的ApplicationContextInitializerApplicationListener

    @SpringBootApplication
    public class SpringBootWebDemoApplication {
        public static void main(String[] args) {
            //SpringApplication.run(SpringBootWebDemoApplication.class, args);
            SpringApplication springApplication = new SpringApplication(SpringBootWebDemoApplication.class);
    
            springApplication.addInitializers(MyApplicationContextInitializer1,MyApplicationContextInitializer2);
    
            springApplication.addListeners(MyApplicationListener1,MyApplicationListener2);
    
            springApplication.run(args);
        }
    }
    

    2.也可以在当前项目的类路径下创建META-INF/spring.factories文件,并声明相应的ApplicationContextInitializerApplicationListener

    org.springframework.context.ApplicationContextInitializer=\
    xxx.xxx.MyApplicationContextInitializer1,\
    xxx.xxx.MyApplicationContextInitializer2
    
    # Application Listeners
    org.springframework.context.ApplicationListener=\
    xxx.xxx.MyApplicationListener1,\
    xxx.xxx.MyApplicationListener2
    

    3.至于ApplicationRunner和CommandLineRunner,只需要在其实现类上加上@Component注解或者在@Configuration配置类中通过@Bean注解注入。


    深入了解SpringBootServletInitializer

    熟悉了SpringApplication的原理之后,我们再来了解SpringBootServletInitializer的原理就比较容易了。

    public class ServletInitializer extends SpringBootServletInitializer {
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(DemoWarApplication.class);
        }
    }
    

    SpringBootServletInitializer就是一个org.springframework.web.context.WebApplicationContext,容器启动时会调用其onStartup(ServletContext servletContext)方法,接下来我么就来看一下这个方法:

    public void onStartup(ServletContext servletContext) throws ServletException {
            this.logger = LogFactory.getLog(this.getClass());
            final WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
            if(rootAppContext != null) {
                servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                    public void contextInitialized(ServletContextEvent event) {
                    }
                });
            } else {
                this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
            }
    
        }
    

    这里的核心方法就是createRootApplicationContext(servletContext):

    protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
    
            //创建SpringApplicationBuilder,并用其生产出SpringApplication对象
            SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
            builder.main(this.getClass());
    
            ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
            if(parent != null) {
                this.logger.info("Root context already created (using as parent).");
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
                builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
            }
    
            //初始化并封装SpringApplicationBuilder对象,为SpringApplication对象增加ApplicationContextInitializer和ApplicationListener做准备
            builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
            builder.listeners(new ApplicationListener[]{new ServletContextApplicationListener(servletContext)});
            //指定创建的ApplicationContext类型
            builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
    
            //传递入口类,并构建SpringApplication对象
            //可以通过configure()方法对SpringBootServletInitializer进行扩展
            builder = this.configure(builder);
            SpringApplication application = builder.build();
    
            if(application.getSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
                application.getSources().add(this.getClass());
            }
    
            Assert.state(!application.getSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
            if(this.registerErrorPageFilter) {
                application.getSources().add(ErrorPageFilter.class);
            }
    
            //最后调用SpringApplication的run方法
            return this.run(application);
        }
    

    说明
    SpringBootServletInitializer的执行过程,简单来说就是通过SpringApplicationBuilder构建并封装SpringApplication对象,并最终调用SpringApplication的run方法的过程。


    扩展SpringBootServletInitializer

    与扩展SpringApplication类似,ApplicationContextInitializerApplicationListener可以基于SpringApplicationBuilder提供的public方法进行扩展

    public class ServletInitializer extends SpringBootServletInitializer {
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            application.initializers(MyApplicationContextInitializer1,MyApplicationContextInitializer2);
            application.listeners(MyApplicationListener1,MyApplicationListener2)
            return application.sources(DemoWarApplication.class);
        }
    
    }
    

    相关文章

      网友评论

      • 5fbe15677d0f:可以转载嘛
        飘逸峰:可以
      • 紫玄:请问ServletInitializer类是否多余呢???因为发现通过idea创建的springboot项目,是没有这个类的,而通过start.spring.io创建的项目是有这个类的,两个项目都可以正常运行.....

      本文标题:Spring Boot学习笔记03--深入了解SpringBoo

      本文链接:https://www.haomeiwen.com/subject/aiwxvttx.html