• Springboot源码——应用程序上下文分析


      前两篇(Spring MVC源码——Root WebApplicationContext 和 Spring MVC源码——Servlet WebApplicationContext)讲述了springmvc项目创建上下文的过程,这一篇带大家了解一下springboot项目创建上下文的过程。

    SpringApplication引导类

    SpringApplication类用于启动或者引导springboot项目,直接应用在java main方法中。

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //判断当前web应用程序类型
        this.webApplicationType = deduceWebApplicationType();
        //找到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        //找到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //获得当前执行main方法的类对象
        this.mainApplicationClass = deduceMainApplicationClass();
    }

    springboot项目WebApplicationType分为三种:非web类型,web类型(spring-mvc),响应式web类型(spring-webflux)

    private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
                "org.springframework.web.context.ConfigurableWebApplicationContext" };
    
    private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
            + "web.reactive.DispatcherHandler";
    
    private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
            + "web.servlet.DispatcherServlet";
    
    private WebApplicationType deduceWebApplicationType() {
        if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
                && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : WEB_ENVIRONMENT_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }

    下面的run方法是springboot项目启动的核心代码。

    public ConfigurableApplicationContext run(String... args) {
        //开启任务执行时间监听器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
    
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
        //设置系统属性『java.awt.headless』,为true则启用headless模式支持
        configureHeadlessProperty();
    
        //通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
        //找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
        //之后逐个调用其started()方法,广播SpringBoot要开始执行了。
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
    
                //创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
            //并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。                
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            //是否搜索BeanInfo类
            configureIgnoreBeanInfo(environment);
            //Banner打印
            Banner printedBanner = printBanner(environment);
    
            //根据WebApplicationType的值来决定创建何种类型的ApplicationContext对象
            context = createApplicationContext();
    
            //通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,获取并实例化异常分析器
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
    
            //为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
            //并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
            //之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
            //这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
    
            //初始化所有自动配置类,调用ApplicationContext的refresh()方法
            refreshContext(context);
            
            
            //空方法
            afterRefresh(context, applicationArguments);
    
            /关闭任务执行时间监听器
            stopWatch.stop();
    
            //如果开启日志,则打印执行是时间
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
    
            //调用所有的SpringApplicationRunListener的started()方法,广播SpringBoot已经完成了ApplicationContext初始化的全部过程。
            listeners.started(context);
    
            //遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
            //我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
    
        try {
            //调用所有的SpringApplicationRunListener的running()方法,广播SpringBoot已经可以处理服务请求了。
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

    由上文可知,默认WebApplicationType是WebApplicationType.SERVLET,所以默认的上下文是AnnotationConfigServletWebServerApplicationContext。

    //应用程序非web环境
    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
            + "annotation.AnnotationConfigApplicationContext";
    
    //应用程序web环境
    public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
    
    //应用程序响应式web环境
    public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
    
    
    public enum WebApplicationType {
    
        //应用程序不需要任何应用服务器
        NONE,
    
        //应用程序内嵌web服务器
        SERVLET,
    
        //应用程序内嵌响应式web服务器
        REACTIVE
    
    }
    
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, "
                                + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

    AnnotationConfigServletWebServerApplicationContext类结构层次如下。

    父类ServletWebServerApplicationContext创建内嵌web应用服务器如下。

    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            //创建web应用服务
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }
    
    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            //获取ServletWebServerFactory类型的web服务器工厂类,比如TomcatServletWebServerFactory,JettyServletWebServerFactory,UndertowServletWebServerFactory
            ServletWebServerFactory factory = getWebServerFactory();
            this.webServer = factory.getWebServer(getSelfInitializer());
        }
        else if (servletContext != null) {
            try {
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context",
                        ex);
            }
        }
        initPropertySources();
    }

    Springmvc项目上下文和Springboot项目上下文浅析

    Springmvc项目上下文

    Springmvc项目的rootcontext的创建时通过 xml中 配置的org.springframework.web.context.ContextLoaderListener,其父类ContextLoader中有一个初始化上下文的方法,如下。
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext);

    上下文创建好之后调用

    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

    其中 servletContext的实例是 org.apache.catalina.core.ApplicationContext;

    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = org.springframework.web.context.WebApplicationContext.ROOT)

    这样rootcontext就创建好了,并且放入了servletContext中保存。rootcontext获取方式如下。 

    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

    Springmvc项目中的DispatcherServlet是在xml中按照servlet格式配置的,这种方式创建的servlet实例没有被spring容器管理。

    DispatcherServlet实现了ApplicationContextAware接口,有一个成员变量来保存此servlet对应的上下文,如下。
    /** WebApplicationContext for this servlet */
    private WebApplicationContext webApplicationContext;

    这种情况下webApplicationContext变量是无法注入的【DispatcherServlet实例没有被spring容器管理】。看一下DispatcherServlet的父类FrameworkServlet是如何初始化上下文的,如下。

    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        //springmvc项目这块的判断为false;springboot项目为ture。
        if (this.webApplicationContext != null) {
    
        ......

    DispatcherServlet所属上下文的存储

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
         ...
         request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
         ...
    }

    DispatcherServlet所属上下文获取org.springframework.web.servlet.support.RequestContextUtils。

    Springboot项目上下文

    Springboot项目中,调试代码时发现DispatcherServlet的父类FrameworkServlet在初始化上下文的时候rootcontext 和 DispatcherServlet成员变量webApplicationContext保存的是一个实例,即AnnotationConfigServletWebServerApplicationContext实例。
    上面也提到了DispatcherServlet【对应的实例被spring容器管理】实现了ApplicationContextAware接口,webApplicationContext保存的上下文是通过自动注入而来。
    RootContext(AnnotationConfigServletWebServerApplicationContext)保存到servletcontext中的操作,如下。
    //ServletWebServerApplicationContext
    protected
    void prepareWebApplicationContext(ServletContext servletContext) { ... servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this); ... }

    总结

    经过三篇文章的分析,相信大家已经明白了Springmvc项目默认是有两个上下文(Root webapplicationcontext 和 Servlet webapplicationcontext,对应的类型是XmlServletWebServerApplicationContext),而Springboot项目默认是一个上下文,对应的类型是AnnotationConfigServletWebServerApplicationContext。如果有什么疑问,请关注订阅号,进行私聊。

     
  • 相关阅读:
    HDU4507 吉哥系列故事――恨7不成妻(数位dp)
    UCF Local Programming Contest 2017 G题(dp)
    ICPC Latin American Regional Contests 2019 I题
    UCF Local Programming Contest 2017 H题(区间dp)
    HDU2089 不要62
    AcWing1084 数字游戏II(数位dp)
    UCF Local Programming Contest 2017 F题(最短路)
    Google Code Jam 2019 Round 1A Pylons(爆搜+贪心)
    AcWing1083 Windy数(数位dp)
    Vue
  • 原文地址:https://www.cnblogs.com/hujunzheng/p/10854464.html
Copyright © 2020-2023  润新知