zoukankan      html  css  js  c++  java
  • 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。如果有什么疑问,请关注订阅号,进行私聊。

     
  • 相关阅读:
    JAVA学习日报 8.26
    JAVA学习日报 8.25
    JAVA学习日报 8.24
    JAVA学习日报 8.23
    Docker 详解
    DRF 3 请求响应异常处理
    DRF 2 序列化器
    DRF 1 API接口规范
    计算机计算小数的方法
    软件结构体系第二章
  • 原文地址:https://www.cnblogs.com/hujunzheng/p/10854464.html
Copyright © 2011-2022 走看看