美文网首页SpringBoot系列
5) 基于JAR方式Spring Boot组件初始化

5) 基于JAR方式Spring Boot组件初始化

作者: 涣涣虚心0215 | 来源:发表于2021-03-03 01:23 被阅读0次

    基于SpingMVC组件初始化那一章节,了解了WebApplicationInitializer和SpringServletContainerInitializer的关系,以及在容器启动过程中的重要作用。
    但是在Spring Boot应用启动的时候,却不能debug到这两个类相关类中,是不是SCI在Spring Boot项目不起作用?
    对于Spring Boot应用来说,并没有使用SpringServletContainerInitializer来进行容器初始化,而是使用了TomcatStarter(也是ServletContainerInitializer的子类)。

    class TomcatStarter implements ServletContainerInitializer {
        private static final Log logger = LogFactory.getLog(TomcatStarter.class);
        private final ServletContextInitializer[] initializers;
        private volatile Exception startUpException;
        TomcatStarter(ServletContextInitializer[] initializers) {
            this.initializers = initializers;
        }
        @Override
        public void onStartup(Set<Class<?>> classes, ServletContext servletContext)
                throws ServletException {
            try {
                for (ServletContextInitializer initializer : this.initializers) {
                    initializer.onStartup(servletContext);
                }
            }
            catch (Exception ex) {
                this.startUpException = ex;
                // Prevent Tomcat from logging and re-throwing when we know we can
                // deal with it in the main thread, but log for information here.
                if (logger.isErrorEnabled()) {
                    logger.error("Error starting Tomcat context. Exception: "
                            + ex.getClass().getName() + ". Message: " + ex.getMessage());
                }
            }
        }
        public Exception getStartUpException() {
            return this.startUpException;
        }
    
    }
    

    从源码上看,TomcatStarter是无法通过SPI来进行加载实例化的:

    • 它所在的Jar包是spring-boot.jar,并没有提供META-INT/services/目录
    • 它的声明是class不是public的
      那么它使怎么启动的呢?
    源码追溯

    SpringApplication源码分析这一章里讲了SpringApplication.run()方法所完成的事情。其内部主要逻辑就是创建AnnotationConfigServletWebServerApplicationContext以及ConfigurableEnvironment环境的准备(Environment主要与PropertySource以及activeProfile相关),最后调用applicationContext的refresh()方法。这里就是很关键的地方。
    AnnotationConfigServletWebServerApplicationContext继承自GenericWebApplicationContext,refresh()方法就在它这里实现。

    @Override
    public final void refresh() throws BeansException, IllegalStateException {
        try {
            //调用abstractApplicationContext里面的refresh()方法,
            //而该refresh()方法会调用onRefresh()方法,即下面的实现
            super.refresh();
        }
        catch (RuntimeException ex) {
            stopAndReleaseWebServer();
            throw ex;
        }
    }
    //abstractApplicationContext会调用子类实现的onRefresh()方法
    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            //创建webServer
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }
    //创建webServer
    private void createWebServer() {
        //一开始webServer和servletContext为null
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            //获取WebServer的工厂类,由不同的厂家实现,这里返回的是TomcatServletWebServerFactory
            ServletWebServerFactory factory = getWebServerFactory();
            //getSelfInitializer会加载所配置的ServletRegistrationBean,FIlterRegistrationBean等
            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();
    }
    //从BeanFactory中获取所配置的ServletRegistrationBean,FIlterRegistrationBean等加入到ServletContext中
    private void selfInitialize(ServletContext servletContext) throws ServletException {
            prepareWebApplicationContext(servletContext);
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
                    beanFactory);
            WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
                    getServletContext());
            existingScopes.restore();
            WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
                    getServletContext());
            for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                beans.onStartup(servletContext);
            }
        }
    }
    

    这部分代码主要就是调用abstractApplicationContext.refresh()方法,以及通过TomcatServletWebServerFactory工厂类创建webServer。

    //TomcatServletWebServerFactory开始
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        Tomcat tomcat = new Tomcat();
        File baseDir = (this.baseDirectory != null) ? this.baseDirectory
                : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        tomcat.getService().addConnector(connector);
        customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        configureEngine(tomcat.getEngine());
        for (Connector additionalConnector : this.additionalTomcatConnectors) {
            tomcat.getService().addConnector(additionalConnector);
        }
        //上面创建tomcat,prepareContext就是准备TomcatEmbeddedContext
        prepareContext(tomcat.getHost(), initializers);
        return getTomcatWebServer(tomcat);
    }
    protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
        File documentRoot = getValidDocumentRoot();
        TomcatEmbeddedContext context = new TomcatEmbeddedContext();
        if (documentRoot != null) {
            context.setResources(new LoaderHidingResourceRoot(context));
        }
        context.setName(getContextPath());
        。。。这里给TomcatEmbeddedContext赋值
        context.addLifecycleListener(new StaticResourceConfigurer(context));
        //mergeInitializers就是添加TomcatStarter所需要的ServletContextInitializer
        ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
        host.addChild(context);
        //创建TomcatStarter的地方
        configureContext(context, initializersToUse);
        postProcessContext(context);
    }
    //添加TomcatStarter所需要的ServletContextInitializer,比如SessionConfiguringInitializer
    protected final ServletContextInitializer[] mergeInitializers(
            ServletContextInitializer... initializers) {
        List<ServletContextInitializer> mergedInitializers = new ArrayList<>();
        mergedInitializers.add((servletContext) -> this.initParameters
                .forEach(servletContext::setInitParameter));
        mergedInitializers.add(new SessionConfiguringInitializer(this.session));
        mergedInitializers.addAll(Arrays.asList(initializers));
        mergedInitializers.addAll(this.initializers);
        return mergedInitializers.toArray(new ServletContextInitializer[0]);
    }
    //创建TomcatStarter的地方
    protected void configureContext(Context context,
            ServletContextInitializer[] initializers) {
        TomcatStarter starter = new TomcatStarter(initializers);
        if (context instanceof TomcatEmbeddedContext) {
            TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
            embeddedContext.setStarter(starter);
            embeddedContext.setFailCtxIfServletStartFails(true);
        }
        context.addServletContainerInitializer(starter, NO_CLASSES);
        for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
            context.addLifecycleListener(lifecycleListener);
        }
        for (Valve valve : this.contextValves) {
            context.getPipeline().addValve(valve);
        }
        for (ErrorPage errorPage : getErrorPages()) {
            new TomcatErrorPage(errorPage).addToContext(context);
        }
        for (MimeMappings.Mapping mapping : getMimeMappings()) {
            context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
        }
        configureSession(context);
        for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
            customizer.customize(context);
        }
    }
    

    从这代码看到TomcatStarter是如何创建的。

    与SpringMVC容器初始化类比
    1. SpringServletContainerInitializer对应TomcatStarter
    2. WebApplicationInitializer对应ServletContextInitializer
    Spring Boot怎么添加Servlet,filter,Listener?

    在上述代码示例中createWebServer的时候会调用selfInitialize(),这里面会调用这些RegistrationBean。
    使用RegistrationBean(ServletRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean)。
    RegistrationBean实现了ServletContextInitializer。

    相关文章

      网友评论

        本文标题:5) 基于JAR方式Spring Boot组件初始化

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