当前位置:首页 > 生活常识 > 正文

Tomcat 在 SpringBoot 中是如何启动的

摘要: Tomcat在SpringBoot中是如何启动的最佳答案53678位专家为你答疑解惑Tomcat在SpringBoot中是如何启...

Tomcat 在 SpringBoot 中是如何启动的

最佳答案 53678位专家为你答疑解惑

Tomcat 在 SpringBoot 中是如何启动的

ReferenceTomcat 在 SpringBoot 中是如何启动的前言

我们知道 SpringBoot 给我们带来了一个全新的开发体验,我们可以把 web 程序打包成jar包,直接启动,这就得益于SpringBoot内置了容器,可以直接启动。本文将以Tomcat为例,来看看SpringBoot是如何启动Tomcat的,同时也将展开学习Tomcat的源码,了解Tomcat的设计。

从 Main 方法说起

用过 SpringBoot 的人都知道,首先要写一个 main方法来启动。

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

我们直接点击 run 方法的源码,跟踪下来,发现 run 方法原来是调用 ConfigurableApplicationContext 方法,源码如下:

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);        configureIgnoreBeanInfo(environment);        //打印banner        Banner printedBanner=printBanner(environment);        //创建应用上下文        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);        //刷新上下文        refreshContext(context);        //再一次刷新上下文,其实是空方法,可能是为了后续扩展。        afterRefresh(context, applicationArguments);        stopWatch.stop();        if (this.logStartupInfo) {            new StartupInfoLogger(this.mainApplicationClass)                    .logStarted(getApplicationLog(), stopWatch);        }        //发布应用已经启动的事件        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 {        //应用已经启动完成的监听事件        listeners.running(context);    }    catch (Throwable ex) {        handleRunFailure(context, ex, exceptionReporters, null);        throw new IllegalStateException(ex);    }    return context;}

其实这个方法可以总结为以下几个步骤:

配置属性获取监听器,发布应用开始启动事件初始化输入参数配置环境,输出 banner创建上下文预处理上下文刷新上下文再刷新上下文发布应用已经启动事件发布应用启动完成事件

其实上面这段代码,如果只分析 Tomcat 内容的话,只需要关注两个部分:上下文是如何创建的,对应方法: createApplicationContext()上下文是如何刷新的,对应方法: refreshContext(context)接下来,我们来看看这两个方法做了什么。

protected ConfigurableApplicationContext createApplicationContext() {    Class<?> contextClass=this.applicationContextClass;    if (contextClass==null) {        try {            switch (this.webApplicationType) {            case SERVLET:                contextClass=Class.forName(DEFAULT_SERVLET_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);}

这里就是根据 webApplicationType 来判断创建哪种类型的 Servlet,代码中分别对应着 Web 类型(SERVLET),响应式 Web 类型(REACTIVE),非 Web 类型(default),我们建立的是 Web 类型,所以肯定实例化 DEFAULT_SERVLET_WEB_CONTEXT_CLASS 指定的类。也就是 AnnotationConfigServletWebServerApplicationContext 类,用图来说明下这个类的关系。

通过这个类图可以知道,这个类继承的是 ServletWebServerApplicationContext,这才是真正的主角,而这个类最终继承了 AbstractApplicationContext,了解完创建上下文的情况后,我们再来看看刷新上下文。

//类: SpringApplicationprivate void refreshContext(ConfigurableApplicationContext context) {    refresh(context);    if (this.registerShutdownHook) {        try {            context.registerShutdownHook();        }        catch (AccessControlException ex) {            // Not allowed in some environments.        }    }}protected void refresh(ApplicationContext applicationContext) {    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);    ((AbstractApplicationContext) applicationContext).refresh();}

这里还是直接传递调用本类的 refresh(context) 方法,最后强转成父类 AbstractApplicationContext,调用其 refresh() 方法,源码如下:

//类: AbstractApplicationContextpublic void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {        // Prepare this context for refreshing.        prepareRefresh();        // Tell the subclass to refresh the internal bean factory.        ConfigurableListableBeanFactory beanFactory=obtainFreshBeanFactory();        // Prepare the bean factory for use in this context.        prepareBeanFactory(beanFactory);        try {            // Allows post-processing of the bean factory in context subclasses.            postProcessBeanFactory(beanFactory);            // Invoke factory processors registered as beans in the context.            invokeBeanFactoryPostProcessors(beanFactory);            // Register bean processors that intercept bean creation.            registerBeanPostProcessors(beanFactory);            // Initialize message source for this context.            initMessageSource();            // Initialize event multicaster for this context.            initApplicationEventMulticaster();            // Initialize other special beans in specific context subclasses.            onRefresh();            // Check for listener beans and register them.            registerListeners();            // Instantiate all remaining (non-lazy-init) singletons.            finishBeanFactoryInitialization(beanFactory);            // Last step: publish corresponding event.            finishRefresh();        }        catch (BeansException ex) {            if (logger.isWarnEnabled()) {                logger.warn("Exception encountered during context initialization - " +                        "cancelling refresh attempt: " + ex);            }            // Destroy already created singletons to avoid dangling resources.            destroyBeans();            // Reset 'active' flag.            cancelRefresh(ex);            // Propagate exception to caller.            throw ex;        }        finally {            // Reset common introspection caches in Spring's core, since we            // might not ever need metadata for singleton beans anymore...            resetCommonCaches();        }    }}

这里,我们看到 onRefresh() 方法是调用其子类的实现,根据我们上文的分析,我们这里的子类是 ServletWebServerApplicationContext

//类: ServletWebServerApplicationContextprotected void onRefresh() {    super.onRefresh();    try {        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 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();}

到这里,终于见到了庐山真面目, createWebServer() 就是启动 web 服务,但是还没有真正启动 Tomcat,既然 webServer 是通过 ServletWebServerFactory 来获取的,那就来看看这个工厂的真面目。

走进 Tomcat 内部

根据上图,我们发现工厂类是一个接口,各个具体服务的实现是由各个子类来完成的,所以,就去看看 TomcatServletWebServerFactory.getWebServer() 的实现。

@Overridepublic 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);    }    prepareContext(tomcat.getHost(), initializers);    return getTomcatWebServer(tomcat);}

根据上面的代码,我们发现其实主要做两件事,第一件事就是把 Connnctor (我们称之为连接器)对象添加到 Tomcat 中;第二件事就是 configureEngine,这连接器我们勉强能理解(不理解后面会述说),那这个 Engine 是什么呢?查看 tomcat.getEngine() 的源码:

public Engine getEngine() {    Service service=getServer().findServices()[0];    if (service.getContainer() !=null) {        return service.getContainer();    }    Engine engine=new StandardEngine();    engine.setName( "Tomcat" );    engine.setDefaultHost(hostname);    engine.setRealm(createDefaultRealm());    service.setContainer(engine);    return engine;}

根据上面的源码,我们发现,原来这个 Engine 是容器,继续跟踪源码,找到 Container 接口。

上图中,我们看到了4个子接口,分别是 EngineHostContextWrapper。我们从继承关系上可以知道他们都是容器,那么他们之间的区别是什么呢?那就从它们的注释来分析分析:

/** * If used, an Engine is always the top level Container in a Catalina * hierarchy. Therefore, the implementation's <code>setParent()</code> method * should throw <code>IllegalArgumentException</code>. * * @author Craig R. McClanahan */public interface Engine extends Container {    //省略代码}/** * <p> * The parent Container attached to a Host is generally an Engine, but may * be some other implementation, or may be omitted if it is not necessary. * <p> * The child containers attached to a Host are generally implementations * of Context (representing an individual servlet context). * * @author Craig R. McClanahan */public interface Host extends Container {    //省略代码}/** * <p> * The parent Container attached to a Context is generally a Host, but may * be some other implementation, or may be omitted if it is not necessary. * <p> * The child containers attached to a Context are generally implementations * of Wrapper (representing individual servlet definitions). * <p> * * @author Craig R. McClanahan */public interface Context extends Container, ContextBind {    //省略代码}/** * <p> * The parent Container attached to a Wrapper will generally be an * implementation of Context, representing the servlet context (and * therefore the web application) within which this servlet executes. * <p> * Child Containers are not allowed on Wrapper implementations, so the * <code>addChild()</code> method should throw an * <code>IllegalArgumentException</code>. * * @author Craig R. McClanahan */public interface Wrapper extends Container {    //省略代码}

上面的注释翻译过来就是:Engine 是最高级别的容器,其子容器是 HostHost 的子容器是 ContextWrapperContext 的子容器,所以这4个容器的关系就是父子关系。也就是:Engine > Host > Context > Wrapper 接着,再看看 Tomcat 的源码:

//部分代码public class Tomcat {    //设置连接器    public void setConnector(Connector connector) {        Service service=getService();        boolean found=false;        for (Connector serviceConnector : service.findConnectors()) {            if (connector==serviceConnector) {                found=true;            }        }        if (!found) {            service.addConnector(connector);        }    }    //获取service    public Service getService() {        return getServer().findServices()[0];    }    //设置Host容器    public void setHost(Host host) {        Engine engine=getEngine();        boolean found=false;        for (Container engineHost : engine.findChildren()) {            if (engineHost==host) {                found=true;            }        }        if (!found) {            engine.addChild(host);        }    }    //获取Engine容器    public Engine getEngine() {        Service service=getServer().findServices()[0];        if (service.getContainer() !=null) {            return service.getContainer();        }        Engine engine=new StandardEngine();        engine.setName( "Tomcat" );        engine.setDefaultHost(hostname);        engine.setRealm(createDefaultRealm());        service.setContainer(engine);        return engine;    }    //获取server    public Server getServer() {        if (server !=null) {            return server;        }        System.setProperty("catalina.useNaming", "false");        server=new StandardServer();        initBaseDir();        server.setPort( -1 );        Service service=new StandardService();        service.setName("Tomcat");        server.addService(service);        return server;    }    //添加context容器    public Context addContext(Host host, String contextPath, String contextName,            String dir) {        silence(host, contextName);        Context ctx=createContext(host, contextPath);        ctx.setName(contextName);        ctx.setPath(contextPath);        ctx.setDocBase(dir);        ctx.addLifecycleListener(new FixContextListener());        if (host==null) {            getHost().addChild(ctx);        } else {            host.addChild(ctx);        }        return ctx;    }    //添加Wrapper容器    public Context addWebapp(Host host, String contextPath, String docBase) {        LifecycleListener listener=null;        try {            Class<?> clazz=Class.forName(getHost().getConfigClass());            listener=(LifecycleListener) clazz.getConstructor().newInstance();        } catch (ReflectiveOperationException e) {            // Wrap in IAE since we can't easily change the method signature to            // to throw the specific checked exceptions            throw new IllegalArgumentException(e);        }        return addWebapp(host,  contextPath, docBase, listener);    }}

阅读 TomcatgetServer() 方法,我们可以知道,Tomcat 的最顶层就是 Server,也就是 Tomcat 的实例,一个 Tomcat 一个 Server;通过 getEngine() 我们可以了解到 Server 下面是 Service,而且是多个,一个 Service 代表我们部署的一个应用,还可以知道,Engine 容器,一个 Service 只有一个;根据父子关系,通过 setHost() 源码可知,host 容器有多个;同理,我们发现 addContext() 源码下,Context 也是多个;addServlet() 表明 Wrapper 容器也是多个,而且这段代码也暗示了,其实 WrapperServlet 是一层意思。另外我们根据 setConnector 源码可以知道,连接器(Connector)是设置在 Service 下的,而且是可以设置多个连接器(Connector)。

根据上面的分析,可以总结出 Tomcat 主要包含了 2 个核心组件:连接器(Connector)和容器(Container),用图表示如下:

一个 Tomcat 是一个 Server,一个 Server 下有多个 Service,也就是我们部署的多个应用,一个应用下有多个连接器(Connector)和一个容器(Container),容器下有多个子容器,关系用图表示如下:

Engine 下有多个 Host 子容器,Host 下有多个 Context 子容器,Context 下有多个 Wrapper 子容器。

总结

SpringBoot 的启动是通过 new SpringApplication() 实例来启动的,启动过程主要做如下几件事情:

配置属性获取监听器,发布应用开始启动事件初始化输入参数配置环境,输出banner创建上下文预处理上下文刷新上下文刷新上下文发布应用已经启动事件发布应用启动完成事件

而启动 Tomcat 就是在第7步的“刷新上下文”;Tomcat 的启动主要是初始化2个核心组件,连接器(Connector)和容器(Container),一个 Tomcat 实例就是一个 Server,一个 Server 包含多个 Service,也就是多个应用程序,每个 Service 包含多个连接器(Connetor)和一个容器(Container),而容器下又有多个子容器,按照父子关系分别为:EngineHostContextWrapper,其中除了 Engine 外,其余的容器都是可以有多个。

Spring Boot 中 Tomcat 是怎么启动的

Spring Boot一个非常突出的优点就是不需要我们额外再部署Servlet容器,它内置了多种容器的支持。我们可以通过配置来指定我们需要的容器。

本文以我们平时最常使用的容器Tomcat为例来介绍以下两个知识点:

Spring Boot是怎么整合启动Tomcat容器的;在Spring Boot中,怎么进行Tomcat的深度配置。

Spring Boot整合启动Tomcat的流程

对于看源代码,每个人都有自己的方法。我自己在看源代码的时候喜欢结合IDEA的Debug功能一起看。比如说现在我们要研究Spring Boot是在哪个环节点启动Tomcat的,我的思路是:Tomcat在启动时会调用各个组件的init方法和start方法,那么我只需要在这些方法上打上端点,然后就能在调用栈上看出Spring Boot是在哪个环节点启用Tomcat的了。

按照这个思路,我在Tomcat的Connector组件的init方法上打了端点,通过调用栈能很清楚地看出Spring Boot是在容器的onRefresh方法中调用Tomcat的。

protected void onRefresh() {    super.onRefresh();    try {        this.createWebServer();    } catch (Throwable var2) {        throw new ApplicationContextException("Unable to start web server", var2);    }}

找到了调用点,那么一切都好办了。从上面的方法中可以看出,重点内容就在this.createWebServer()这个方法中。

在Spring Boot中使用的容器类是ServletWebServerApplicationContext系列的容器,这个系列的容器可以内嵌Web容器。这个我们可以从这个容器的属性和方法中可以看出来。

public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {    //...省略部分代码    public static final String DISPATCHER_SERVLET_NAME="dispatcherServlet";    //内嵌容器    private volatile WebServer webServer;    private ServletConfig servletConfig;       //...省略部分代码    //创建Web容器    private void createWebServer() {        WebServer webServer=this.webServer;        ServletContext servletContext=this.getServletContext();        //webServer和servletContext都是null,表示还没创建容器,进入创建容器的逻辑        if (webServer==null && servletContext==null) {            //获取创建容器的工厂,可以通过WebServerFactoryCustomizer接口对这个工厂进行自定义设置            ServletWebServerFactory factory=this.getWebServerFactory();            //具体的创建容器的方法,我们进去具体看下            this.webServer=factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});        } else if (servletContext !=null) {            try {                this.getSelfInitializer().onStartup(servletContext);            } catch (ServletException var4) {                throw new ApplicationContextException("Cannot initialize servlet context", var4);            }        }        this.initPropertySources();    }}

下面是TomcatServletWebServerFactory的getWebServer方法。

public class TomcatServletWebServerFactory的getWebServer{//...@Overridepublic WebServer getWebServer(ServletContextInitializer... initializers) {    //创建Tomcat容器    Tomcat tomcat=new Tomcat();    File baseDir=(this.baseDirectory !=null ? this.baseDirectory            : createTempDir("tomcat"));    tomcat.setBaseDir(baseDir.getAbsolutePath());    //创建连接器,默认NIO模式,可以通过WebServerFactoryCustomizer改变具体模式    Connector connector=new Connector(this.protocol);    tomcat.getService().addConnector(connector);    //自定义连接器    customizeConnector(connector);    tomcat.setConnector(connector);    tomcat.getHost().setAutoDeploy(false);    configureEngine(tomcat.getEngine());    //可以通过WebServerFactoryCustomizer添加额外的连接器,这边将这些连接器绑定到Tomcat    for (Connector additionalConnector : this.additionalTomcatConnectors) {        tomcat.getService().addConnector(additionalConnector);    }    //组测Listener、Filter和Servlet,自定义Context等操作    //这个方法可以重点看下    prepareContext(tomcat.getHost(), initializers);    //创建TomcatWebServer,并调用start方法    return getTomcatWebServer(tomcat);}//内嵌的Tomcat容器public TomcatWebServer(Tomcat tomcat, boolean autoStart) {Assert.notNull(tomcat, "Tomcat Server must not be null");this.tomcat=tomcat;this.autoStart=autoStart;        //这边触发Tomcat的启动流程,是Tomcat启动的入口点initialize();}//...省略部分代码}

至此Spring Boot内嵌的Tomcat已将顺序启动了。那么Spring Boot是在什么时候注册DispatchServlet的呢?

配置Listener、Filter和Servlet

Spring Boot配置Listener、Filter和Servlet可以参考我之前写的文章Spring Boot使用嵌入式容器,那怎么配置自定义Filter呢

推荐使用ServletListenerRegistrationBean、FilterRegistrationBean和ServletRegistrationBean的方式注册Listener、Filter和Servlet。

Spring Boot注册DispatcherServlet

在传统的Spring MVC项目中,我们都会在web.xml中注册DispatcherServlet这个入口类,那么在Spring Boot中是在哪里注册的呢?

大家如果看Spring Boot的源代码,这边有个小技巧大家可以参考下。就是Spring Boot把之前传统项目中的配置项都通过AutoConfig的形式做配置了。所以这边在寻找DispatcherServlet是在哪里配置的也可以顺着这个思路去寻找。

在IDEA的类查找功能中输入DispatcherServlet关键字,我们能看到一个DispatcherServletAutoConfiguration类。从名字上就能看出这个类是DispatcherServlet的自动配置类,我们点进去看下是否是在这个类内部注册的DispatcherServlet?

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@Configuration@ConditionalOnWebApplication(type=Type.SERVLET)@ConditionalOnClass(DispatcherServlet.class)@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)@EnableConfigurationProperties(ServerProperties.class)public class DispatcherServletAutoConfiguration {/* * The bean name for a DispatcherServlet that will be mapped to the root URL "/" */public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME="dispatcherServlet";/* * The bean name for a ServletRegistrationBean for the DispatcherServlet "/" */public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME="dispatcherServletRegistration";@Configuration@Conditional(DefaultDispatcherServletCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)protected static class DispatcherServletConfiguration {private final WebMvcProperties webMvcProperties;public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {this.webMvcProperties=webMvcProperties;}@Bean(name=DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet() {DispatcherServlet dispatcherServlet=new DispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());return dispatcherServlet;}@Bean@ConditionalOnBean(MultipartResolver.class)@ConditionalOnMissingBean(name=DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)public MultipartResolver multipartResolver(MultipartResolver resolver) {// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;}}@Configuration@Conditional(DispatcherServletRegistrationCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)@Import(DispatcherServletConfiguration.class)protected static class DispatcherServletRegistrationConfiguration {private final ServerProperties serverProperties;private final WebMvcProperties webMvcProperties;private final MultipartConfigElement multipartConfig;public DispatcherServletRegistrationConfiguration(ServerProperties serverProperties, WebMvcProperties webMvcProperties,ObjectProvider<MultipartConfigElement> multipartConfigProvider) {this.serverProperties=serverProperties;this.webMvcProperties=webMvcProperties;this.multipartConfig=multipartConfigProvider.getIfAvailable();}        //很熟悉的代码有没有,ServletRegistrationBean就是我们上一节中介绍的注册Servlet的方式        //只不过这边注册的是DispatcherServlet这个特殊的Servlet@Bean(name=DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value=DispatcherServlet.class, name=DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {ServletRegistrationBean<DispatcherServlet> registration=new ServletRegistrationBean<>(dispatcherServlet,this.serverProperties.getServlet().getServletMapping());registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());if (this.multipartConfig !=null) {registration.setMultipartConfig(this.multipartConfig);}return registration;}}//...省略部分代码}

好了通过这边的介绍,我们直到DispatcherServlet是通过DispatcherServletAutoConfiguration这个自动配置类注册的。

Spring Boot中关于Tomcat的一些其他配置

server.tomcat.accept-count

100.0

Maximum queue length for incoming connection requests when all possible request processing threads are in use.(backlog的长度)

server.tomcat.accesslog.buffered

true

Whether to buffer output such that it is flushed only periodically.

server.tomcat.accesslog.check-exists

false

Whether to check for log file existence so it can be recreated it if an external process has renamed it.

server.tomcat.accesslog.condition-if

Whether logging of the request will only be enabled if "ServletRequest.getAttribute(conditionIf)" does not yield null.

server.tomcat.accesslog.condition-unless

Whether logging of the request will only be enabled if "ServletRequest.getAttribute(conditionUnless)" yield null.

server.tomcat.accesslog.directory

logs

Directory in which log files are created. Can be absolute or relative to the Tomcat base dir.

server.tomcat.accesslog.enabled

false

Enable access log.

server.tomcat.accesslog.encoding

Character set used by the log file. Default to the system default character set.

server.tomcat.accesslog.file-date-format

.yyyy-MM-dd

Date format to place in the log file name.

server.tomcat.accesslog.ipv6-canonical

false

Whether to use IPv6 canonical representation format as defined by RFC 5952.

server.tomcat.accesslog.locale

Locale used to format timestamps in log entries and in log file name suffix. Default to the default locale of the Java process.

server.tomcat.accesslog.max-days

-1.0

Number of days to retain the access log files before they are removed.

server.tomcat.accesslog.pattern

common

Format pattern for access logs.

server.tomcat.accesslog.prefix

access_log

Log file name prefix.

server.tomcat.accesslog.rename-on-rotate

false

Whether to defer inclusion of the date stamp in the file name until rotate time.

server.tomcat.accesslog.request-attributes-enabled

false

Set request attributes for the IP address, Hostname, protocol, and port used for the request.

server.tomcat.accesslog.rotate

true

Whether to enable access log rotation.

server.tomcat.accesslog.suffix

.log

Log file name suffix.

server.tomcat.additional-tld-skip-patterns

Comma-separated list of additional patterns that match jars to ignore for TLD scanning. The special '?' and '*' characters can be used in the pattern to match one and only one character and zero or more characters respectively.

server.tomcat.background-processor-delay

10s

Delay between the invocation of backgroundProcess methods. If a duration suffix is not specified, seconds will be used.

server.tomcat.basedir

Tomcat base directory. If not specified, a temporary directory is used.

server.tomcat.connection-timeout

Amount of time the connector will wait, after accepting a connection, for the request URI line to be presented.

server.tomcat.max-connections

8192.0

Maximum number of connections that the server accepts and processes at any given time. Once the limit has been reached, the operating system may still accept connections based on the "acceptCount" property.

server.tomcat.max-http-form-post-size

2MB

Maximum size of the form content in any HTTP post request.

server.tomcat.max-swallow-size

2MB

Maximum amount of request body to swallow.

server.tomcat.mbeanregistry.enabled

false

Whether Tomcat's MBean Registry should be enabled.

server.tomcat.processor-cache

200.0

Maximum number of idle processors that will be retained in the cache and reused with a subsequent request. When set to -1 the cache will be unlimited with a theoretical maximum size equal to the maximum number of connections.

server.tomcat.redirect-context-root

true

Whether requests to the context root should be redirected by appending a / to the path.

server.tomcat.relaxed-path-chars

Comma-separated list of additional unencoded characters that should be allowed in URI paths. Only "< > [ \ ] ^ ` { | }" are allowed.

server.tomcat.relaxed-query-chars

Comma-separated list of additional unencoded characters that should be allowed in URI query strings. Only "< > [ \ ] ^ ` { | }" are allowed.

server.tomcat.remoteip.host-header

X-Forwarded-Host

Name of the HTTP header from which the remote host is extracted.

server.tomcat.remoteip.internal-proxies

Regular expression that matches proxies that are to be trusted.

server.tomcat.remoteip.port-header

X-Forwarded-Port

Name of the HTTP header used to override the original port value.

server.tomcat.remoteip.protocol-header

Header that holds the incoming protocol, usually named "X-Forwarded-Proto".

server.tomcat.remoteip.protocol-header-https-value

https

Value of the protocol header indicating whether the incoming request uses SSL.

server.tomcat.remoteip.remote-ip-header

Name of the HTTP header from which the remote IP is extracted. For instance, X-FORWARDED-FOR.

server.tomcat.resource.allow-caching

true

Whether static resource caching is permitted for this web application.

server.tomcat.resource.cache-ttl

Time-to-live of the static resource cache.

server.tomcat.threads.max

200.0

Maximum amount of worker threads.

server.tomcat.threads.min-spare

10.0

Minimum amount of worker threads.

server.tomcat.uri-encoding

UTF-8

Character encoding to use to decode the URI.

server.tomcat.use-relative-redirects

false

Whether HTTP 1.1 and later location headers generated by a call to sendRedirect will use relative or absolute redirects.

这边给出一个配置的例子

server:  port: ${port:9999}  tomcat:    accept-count: 200    #最好进行这段配置,默认会在tmp目录下创建,Linux有时会有定时任务删除tmp目录下的内容    basedir: my-tomcat    accesslog:      enabled: true      pattern: '%t %a "%r" %s %S (%b M) (%D ms)'      max-http-post-size: 2MB      max-swallow-size: 2M    uri-encoding: GBK    threads:      max: 100      min-spare: 10

具体使用时可以参考Spring Boo官网关于Tomcat的配置。

一些其他类

Spring Boot还提供了很多自定义类,让用户对Tomcat的组件做自定义配置。这个符合Spring的设计哲学:只提供选择,而不是强制用户使用某项技术。

关于Tomcat的自定义配置类还有以下几个,大家可以按需使用。

WebServerFactoryCustomizer接口:自定义Web容易工厂WebServerFactoryCustomizerBeanPostProcessor处理类:WebServerFactoryCustomizer类通过WebServerFactoryCustomizerBeanPostProcessor类生效TomcatConnectorCustomizer:连接器自定义处理类TomcatContextCustomizer:Context自定义接口

发表评论