GVKun编程网logo

Spring Boot:无法启动嵌入式Tomcat Servlet容器(springboot启动不了tomcat)

25

本文将分享SpringBoot:无法启动嵌入式TomcatServlet容器的详细内容,并且还将对springboot启动不了tomcat进行详尽解释,此外,我们还将为大家带来关于17.SpringB

本文将分享Spring Boot:无法启动嵌入式Tomcat Servlet容器的详细内容,并且还将对springboot启动不了tomcat进行详尽解释,此外,我们还将为大家带来关于17. Spring Boot 配置嵌入式Servlet容器、4_7.springboot2.x嵌入式servlet容器自动配置原理、4、SpringBoot嵌入式Servlet容器、Spring Boot 2.x 嵌入式 Servlet 容器的相关知识,希望对你有所帮助。

本文目录一览:

Spring Boot:无法启动嵌入式Tomcat Servlet容器(springboot启动不了tomcat)

Spring Boot:无法启动嵌入式Tomcat Servlet容器(springboot启动不了tomcat)

我是Spring Boot的新手,运行应用程序时出错。我正在学习一个教程,我相信我对POM有正确的父母和依赖关系,请帮助我

主类:

package com.boot;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;/** * Hello world! * */@SpringBootApplicationpublic class App {    public static void main( String[] args )    {        SpringApplication.run(App.class, "hello");    }}

错误是:

   org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Tomcat servlet container        at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:165) ~[spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.startEmbeddedServletContainer(EmbeddedWebApplicationContext.java:293) ~[spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:141) ~[spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~[spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) ~[spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:764) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        at org.springframework.boot.SpringApplication.doRun(SpringApplication.java:357) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        at org.springframework.boot.SpringApplication.run(SpringApplication.java:305) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1124) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1113) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        at com.boot.App.main(App.java:18) [classes/:na]Caused by: java.lang.IllegalStateException: Tomcat connector in failed state        at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:159) ~[spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]        ... 10 common frames omitted

聚甲醛

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">      <modelVersion>4.0.0</modelVersion>  <groupId>com.boot</groupId>  <artifactId>das-boot</artifactId>  <version>0.0.1-SNAPSHOT</version>  <packaging>jar</packaging>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>1.3.1.RELEASE</version>    </parent>  <name>das-boot</name>  <url>http://maven.apache.org</url>  <properties>    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  </properties>  <dependencies>  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-web</artifactId>  </dependency>    <dependency>      <groupId>junit</groupId>      <artifactId>junit</artifactId>      <version>3.8.1</version>      <scope>test</scope>    </dependency>  </dependencies></project>

答案1

小编典典

尝试将application.yaml(或application.properties)中的端口号更改为其他名称。

17. Spring Boot 配置嵌入式Servlet容器

17. Spring Boot 配置嵌入式Servlet容器

一、如何定制和修改Servlet容器的相关配置


 

1、配置文件(ServerProperties);

优先级最高

server.port=8081
server.context‐path=/crud
server.tomcat.uri‐encoding=UTF‐8
//通用的Servlet容器设置
server.xxx
//Tomcat的设置
server.tomcat.xxx

 

2、java代码

2.1 Spring Boot 1.5.10 版本 

@Bean //一定要将这个定制器加入到容器中 嵌入式的Servlet容器定制器
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
  return new EmbeddedServletContainerCustomizer() {
    //定制嵌入式的Servlet容器相关的规则
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
      container.setPort(8083);
    }
  };
}

 

2.2 Spring Boot 2.1.0版本

 2.2.1、 方式1  (优先级第二)

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }
}

 

  2.2.2、方式2 (优先级最低)

@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.setPort(9000);
    // factory.setSessionTimeout(10, TimeUnit.MINUTES);  此方法没找到
    factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
    return factory;
}

 

二、自定义Servlet容器


 

1、Spring Boot 支持的 Servlet容器类型

 

 2、切换Tomcat容器为jetty容器

 

 

 

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions><!-- 排除掉默认的Tomcat web容器-->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 引入jetty容器。jetty 适合长连接的场景-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId><!--  spring-boot-starter-undertow ,不支持JSP-->
</dependency>

 

undertow

 

 

包下:spring-boot-autoconfigure-2.1.0.RELEASE.jar

 

 

三、嵌入式Servlet容器自动配置原理Spring Boot 2.1.0版本示例: (注:1.5.10 和2.1.0版本有很大不同)


 

 Servelt容器的自动配置类

@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {

    /**
     * Nested configuration if Tomcat is being used.
     */
    @Configuration
    @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
    public static class TomcatWebServerFactoryCustomizerConfiguration {

        @Bean
        public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(
                Environment environment, ServerProperties serverProperties) {
            return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
        }

    }

    /**
     * Nested configuration if Jetty is being used.
     */
    @Configuration
    @ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class })
    public static class JettyWebServerFactoryCustomizerConfiguration {

        @Bean
        public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(
                Environment environment, ServerProperties serverProperties) {
            return new JettyWebServerFactoryCustomizer(environment, serverProperties);
        }

    }

    /**
     * Nested configuration if Undertow is being used.
     */
    @Configuration
    @ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })
    public static class UndertowWebServerFactoryCustomizerConfiguration {

        @Bean
        public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(
                Environment environment, ServerProperties serverProperties) {
            return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
        }

    }

    /**
     * Nested configuration if Netty is being used.
     */
    @Configuration
    @ConditionalOnClass(HttpServer.class)
    public static class NettyWebServerFactoryCustomizerConfiguration {

        @Bean
        public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(
                Environment environment, ServerProperties serverProperties) {
            return new NettyWebServerFactoryCustomizer(environment, serverProperties);
        }

    }

}

 

 Tomcat 的Server的定制(Jetty、Netty、Undertow 类似)

public class TomcatWebServerFactoryCustomizer implements
        WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {

    private final Environment environment;

    private final ServerProperties serverProperties;

    public TomcatWebServerFactoryCustomizer(Environment environment,
            ServerProperties serverProperties) {
        this.environment = environment;
        this.serverProperties = serverProperties;
    }

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public void customize(ConfigurableTomcatWebServerFactory factory) {
        ServerProperties properties = this.serverProperties;
        ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
        PropertyMapper propertyMapper = PropertyMapper.get();
        propertyMapper.from(tomcatProperties::getBasedir).whenNonNull()
                .to(factory::setBaseDirectory);
        propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull()
                .as(Duration::getSeconds).as(Long::intValue)
                .to(factory::setBackgroundProcessorDelay);
        customizeRemoteIpValve(factory);
        propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive)
                .to((maxThreads) -> customizeMaxThreads(factory,
                        tomcatProperties.getMaxThreads()));
        propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive)
                .to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));
        propertyMapper.from(this::determineMaxHttpHeaderSize).whenNonNull()
                .asInt(DataSize::toBytes).when(this::isPositive)
                .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
                        maxHttpHeaderSize));
        propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull()
                .asInt(DataSize::toBytes)
                .to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize));
        propertyMapper.from(tomcatProperties::getMaxHttpPostSize).asInt(DataSize::toBytes)
                .when((maxHttpPostSize) -> maxHttpPostSize != 0)
                .to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,
                        maxHttpPostSize));
        propertyMapper.from(tomcatProperties::getAccesslog)
                .when(ServerProperties.Tomcat.Accesslog::isEnabled)
                .to((enabled) -> customizeAccessLog(factory));
        propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull()
                .to(factory::setUriEncoding);
        propertyMapper.from(properties::getConnectionTimeout).whenNonNull()
                .to((connectionTimeout) -> customizeConnectionTimeout(factory,
                        connectionTimeout));
        propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)
                .to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
        propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)
                .to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
        customizeStaticResources(factory);
        customizeErrorReportValve(properties.getError(), factory);
    }
。。。。。。
}

 

 

Spring Boot 2.1.0 版本的web容器启动流程 (Spring boot 1.5.10版本类似但不同)

 

1.Spring boot启动器

@SpringBootApplication
public class SpringBootDemo01Application {

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

 

2.run方法启动过程源码分析

public class SpringApplication {
   //在createApplicationContext()中根据类型classforName()加载 不同类型的  
    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
            + "annotation.AnnotationConfigApplicationContext";

    public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

    public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

   //run1调用run2
public static ConfigurableApplicationContext run(Class<?> primarySource,String... args) { return run(new Class<?>[] { primarySource }, args); }    //run2调用run3 public static ConfigurableApplicationContext run(Class<?>[] primarySources,String[] args) { return new SpringApplication(primarySources).run(args); }
   //run3启动Web服务器
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); 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); 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; } 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); } private 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(); } }

 

 

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
        
            @Override
    public 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();
            }
        }
    }
    protected void onRefresh() throws BeansException {
        // For subclasses: do nothing by default.
    }
    
}

 

 

public class ServletWebServerApplicationContext extends GenericWebApplicationContext
        implements ConfigurableWebServerApplicationContext {
        
    @Override
    protected 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();
    }
        
}

 

 

 

spring-boot-2.1.0.RELEASE.jar

 

4_7.springboot2.x嵌入式servlet容器自动配置原理

4_7.springboot2.x嵌入式servlet容器自动配置原理

概述

Spring Boot对所支持的Servlet Web服务器实现做了建模抽象:

Servlet容器类型  WebServer模型接口 WebServer工厂实现类
Tomcat    TomcatWebServer     TomcatServletWebServerFactory
Jetty    JettyWebServer    JettyServletWebServerFactory
Undertow  UndertowWebServer  UndertowServletWebServerFactory

基于此模型概念,在一个Servlet Web应用中,Spring Boot会使用上表中所说的WebServer工厂组件生成相应的WebServer实例。而这里的WebServer工厂组件又是从哪里来的呢 ? 这就是自动配置类ServletWebServerFactoryAutoConfiguration的任务了。

自动配置类ServletWebServerFactoryAutoConfiguration首先通过注解声明自己的生效条件:类 ServletRequest 存在于 classpath 上时才生效,也就是要求javax.servlet-api包必须被引用;当前应用必须是Spring MVC应用才生效;在以上条件被满足时,ServletWebServerFactoryAutoConfiguration引入了如下三个配置类 :

EmbeddedTomcat

EmbeddedJetty

EmbeddedUndertow

这三个配置类是ServletWebServerFactoryConfiguration的嵌套配置类,它们会分别检测classpath上存在的类,从而判断当前应用使用的是Tomcat/Jetty/Undertow中的哪一个Servlet Web服务器,从而决定定义相应的工厂组件bean : TomcatServletWebServerFactory/JettyServletWebServerFactory/UndertowServletWebServerFactory。

详解:

ServletWebServerFactoryAutoConfiguration

/**
* EnableAutoConfiguration Auto-configuration for servlet web servers.
*
*/
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 仅在类 ServletRequest 存在于 classpath 上时才生效
@ConditionalOnClass(ServletRequest.class)
// 仅在当前应用是 Servlet Web 应用时才生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 确保前缀为 server 的配置参数加载到 bean ServerProperties
@EnableConfigurationProperties(ServerProperties.class)
// 1. 导入 ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar 以注册
// BeanPostProcessor : WebServerFactoryCustomizerBeanPostProcessor 和
// ErrorPageRegistrarBeanPostProcessor
// 2. 导入 EmbeddedTomcat/EmbeddedJetty/EmbeddedUndertow 这三个属于
// ServletWebServerFactoryConfiguration 的嵌套配置类,这三个配置类会分别检测
// classpath上存在的类,从而判断当前应用使用的是 Tomcat/Jetty/Undertow,
// 从而决定定义哪一个 Servlet Web服务器的工厂 bean :
// TomcatServletWebServerFactory/JettyServletWebServerFactory/UndertowServletWebServerFactory
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {


    // 定义 bean ServletWebServerFactoryCustomizer,这里与properties中serverProperties配置联系,解释了为什么配置文件也可修改servlet容器配置
    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }


    // 针对当前Servlet容器是Tomcat时定义该 bean,用于定制化 TomcatServletWebServerFactory
    @Bean
    // 仅在类 org.apache.catalina.startup.Tomcat 存在于 classpath 上时才生效
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }


    /**
     * Registers a WebServerFactoryCustomizerBeanPostProcessor. Registered via
     * ImportBeanDefinitionRegistrar for early registration.
     * 这是一个 ImportBeanDefinitionRegistrar, 它会向容器注入两个 BeanPostProcessor :
     * 1. WebServerFactoryCustomizerBeanPostProcessor
     * 该 BeanPostProcessor 会搜集容器中所有的 WebServerFactoryCustomizer,对当前应用所采用的
     * WebServerFactory 被初始化前进行定制
     * 2. ErrorPageRegistrarBeanPostProcessor
     * 该 BeanPostProcessor 会搜集容器中所有的 ErrorPageRegistrar,添加到当前应用所采用的
     * ErrorPageRegistry 中,实际上,这里的 ErrorPageRegistry 会是 ConfigurableWebServerFactory,
     * 具体实现上来讲,是一个 ConfigurableTomcatWebServerFactory,ConfigurableJettyWebServerFactory
     * 或者 ConfigurableUndertowWebServerFactory,分别对应 Tomcat, Jetty, Undertow 这三种
     * Servlet Web 容器的工厂类
     */
    public static class BeanPostProcessorsRegistrar
            implements ImportBeanDefinitionRegistrar, BeanFactoryAware {


        private ConfigurableListableBeanFactory beanFactory;


        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            if (beanFactory instanceof ConfigurableListableBeanFactory) {
                this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
            }
        }


        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                            BeanDefinitionRegistry registry) {
            if (this.beanFactory == null) {
                return;
            }
            registerSyntheticBeanIfMissing(registry,
                    "webServerFactoryCustomizerBeanPostProcessor",
                    WebServerFactoryCustomizerBeanPostProcessor.class);
            registerSyntheticBeanIfMissing(registry,
                    "errorPageRegistrarBeanPostProcessor",
                    ErrorPageRegistrarBeanPostProcessor.class);
        }


        private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
                                                    String name, Class<?> beanClass) {
            if (ObjectUtils.isEmpty(
                    this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
                RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
                beanDefinition.setSynthetic(true);
                registry.registerBeanDefinition(name, beanDefinition);
            }
        }


    }

ServletWebServerFactoryConfiguration

       ServletWebServerFactoryConfiguration是一个针对ServletWebServerFactory进行配置的配置类。它通过检测应用classpath存在的类,从而判断当前应用要使用哪个Servlet容器:Tomcat,Jetty还是Undertow。检测出来之后,定义相应的Servlet Web服务器工厂组件bean 

Servlet容器类型  WebServerFactory实现类
Tomcat  TomcatServletWebServerFactory
Jetty     JettyServletWebServerFactory
Undertow     UndertowServletWebServerFactory

注意,以上三个实现类都继承自抽象基类AbstractServletWebServerFactory,实现了接口WebServerFactory,ErrorPageRegistry。该特征会被WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor使用,用于对WebServerFactory进行定制化,以及设置相应的错误页面。

@Configuration
class ServletWebServerFactoryConfiguration {

@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {

@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}

}

/**
* Nested configuration if Jetty is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {

@Bean
public JettyServletWebServerFactory JettyServletWebServerFactory() {
return new JettyServletWebServerFactory();
}

}

/**
* Nested configuration if Undertow is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {

@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
return new UndertowServletWebServerFactory();
}

}

}

查看代码:

这里以TomcatServletWebServerFactory:

注意这里此方法返回值:getTomcatWebServer(tomcat);查看此方法:传入两个参数,只要得到端口号大于0就默认启动

同时查看:其中TomcatWebServer类构造方法:

这时候tomcat启动开始。查看initialize();方法

private void initialize() throws WebServerException {
		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
				addInstanceIdToEngineName();

				Context context = findContext();
				context.addLifecycleListener((event) -> {
					if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
						// Remove service connectors so that protocol binding doesn''t
						// happen when the service is started.
						removeServiceConnectors();
					}
				});

				// Start the server to trigger initialization listeners
				this.tomcat.start();

				// We can re-throw failure exception directly in the main thread
				rethrowDeferredStartupExceptions();

				try {
					ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
				}
				catch (NamingException ex) {
					// Naming is not enabled. Continue
				}

				// Unlike Jetty, all Tomcat threads are daemon threads. We create a
				// blocking non-daemon to stop immediate shutdown
				startDaemonAwaitThread();
			}
			catch (Exception ex) {
				stopSilently();
				destroySilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}

可以看到有启动: this.tomcat.start();启动!

思考

如何对嵌入式容器的配置修改是怎么生效的

两种方法:1、修改properties 

2、配置servlet容器定制器

@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> myWebServerFactoryCustomizer() {
    return new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {
        public void customize(TomcatServletWebServerFactory factory) {
            //设置相关配置
            factory.setPort(8080);
        }
    };
}

思考:怎么修改的原理?

springboot版本对照:

1.5.x:-->

//导入后置处理器的注册器,作用给容器导入组件

@Import(BeanPostProcessorsRegistrar.class

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
//导入后置处理器的注册器
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {.....}

2.x:将其作为内部类导入组件

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
      ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
      ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
      ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    .....
    public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
    ...

        @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
       if (this.beanFactory == null) {
      return;
           }
        //导入servlet定制器的后置处理器    
       registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
       WebServerFactoryCustomizerBeanPostProcessor.class);
       registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
         ErrorPageRegistrarBeanPostProcessor.class);
            }
     }


}

观察方法:BeanPostProcessorsRegistrar,导入servlet定制器的后置处理器 

作用:后置处理器:bean初始化前后(创建完对象,还没赋值赋值)执行初始化工作

WebServerFactoryCustomizerBeanPostProcessor

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   if (bean instanceof WebServerFactory) {
      postProcessBeforeInitialization((WebServerFactory) bean);
   }
   return bean;
}

初始化之前:判断如果当前初始化组件是WebServerFactory 的一个组件,就调用postProcessBeforeInitialization此方法,

private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}

此方法:获取所有的定制器,调用每一个定制器的customize方法,来给servlet容器进行属性赋值

getCustomizers()方法以上调用:


private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}

this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());此方法点进去:
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}

作用:从容器中获取所有该类型的组件:WebServerFactoryCustomizer这里该组件我们可以定制。

思考2?在properties中是怎样定制servlet容器的??

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
      ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
      ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
      ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {......}

2.x:中直接在servelt容器工厂的自动配置类:@EnableConfigurationProperties(ServerProperties.class)注解,向容器中添加ServerProperties组件,在SpringBoot中xxxProperties类都是和配置文件映射的类,用户可以通过配置文件配置类中属性。例如:用户可以通过server.port配置容器端口号。

1.5.x:serverProperties中实现方法。

嵌入式servlet容器自动配置原理的步骤

1)、SpringBoot根据导入的依赖情况,容器中导入的ServletWebServerFactoryConfiguration配置类,给容器添加相应的TomcatServletWebServerFactory,通过TomcatServletWebServerFactory的getWebServer()启动servelt容器

2)、容器中某个组件要创建对象就会惊动后置处理器;@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,

只要是嵌入式的Servlet容器工厂,后置处理器就工作,用与定制嵌入式容器的配置修改,

3)、后置处理器,执行postProcessBeforeInitialization()方法,调用定制器的定制方法;

 

4、SpringBoot嵌入式Servlet容器

4、SpringBoot嵌入式Servlet容器

1、切换嵌入式Servlet容器

  • 默认支持的web服务器webServer:
 Tomcat, Jetty,  Undertow
 ServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并引导创建服务器
  • 切换服务器(可以切换四种)
    image.png

    切换的方式:
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
              <exclusions>
                  <exclusion> 排除
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-tomcat</artifactId>
                  </exclusion>
              </exclusions>
          </dependency>
          <dependency>  导入
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-undertow</artifactId>
          </dependency>
              
              
    2022-07-17 19:34:02.650  INFO 6808 --- [  restartedMain] o.s.b.w.e.undertow.UndertowWebServer     : Undertow started on port(s) 8080 (http)

原理

  • SpringBoot应用启动发现当前是Web应用。因为导了web场景包,它里面也导入tomcat
  • web应用会创建一个web版的ioc容器,名字叫 ServletWebServerApplicationContext
  • ServletWebServerApplicationContext 它在项目一启动的时候寻找 ServletWebServerFactory(Servlet 的web服务器工厂---> 这个工厂生产 Servlet 的web服务器)
  • SpringBoot底层默认有很多的WebServer工厂:TomcatServletWebServerFactory, JettyServletWebServerFactory, UndertowServletWebServerFactory
  • 这些web服务器工厂不需要我们配,底层直接会有一个自动配置类ServletWebServerFactoryAutoConfiguration
  • ServletWebServerFactoryAutoConfiguration导入了ServletWebServerFactoryConfiguration(配置类)
  • ServletWebServerFactoryConfiguration 配置类 根据动态判断系统中到底导入了哪个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有 TomcatServletWebServerFactory
  • TomcatServletWebServerFactory 创建出Tomcat服务器TomcatWebServer并启动;TomcatWebServer 的构造器拥有初始化方法initialize,这个初始化方法把所有东西准备好,把tomcat调用start方法this.tomcat.start();启动tomcat
  • 其实内嵌服务器,就是手动把启动服务器的代码调用(前提是tomcat核心jar包存在,才能启动tomcat)

2、定制Servlet容器

  • 实现 WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
    ○ 把配置文件的值和ServletWebServerFactory 进行绑定
  • 修改配置文件 server.xxx
  • 直接自定义 ConfigurableServletWebServerFactory

xxxxxCustomizer:定制化器,可以改变xxxx的默认规则

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}

Spring Boot 2.x 嵌入式 Servlet 容器

Spring Boot 2.x 嵌入式 Servlet 容器

一、修改Spring Boot 中内嵌Servlet容器的默认配置
Spring Boot提供了默认提供了内嵌的Tomcat,可以通过命令行运行jar包的方式运行Spring Boot应用,spring-boot-start-web依赖中包含了Tomcat依赖,最新版本为9.0.62。

图片

以往通过Tomcat容器运行Spring应用时,可以通过修改Tomcat的一些配置文件来修改Tomcat的运行状态,比如说端口号等,那么Spring Boot内嵌的Tomcat要如何修改?
有两种方式修改内置Tomcat的配置,第一种是在application.properties中添加tomcat配置和server配置

图片

第二种方式是编写一个WebServerFactoryCustomizer来进行tomcat的配置,在SpringBoot1.x的时候使用的是EmbeddedServletContainerCustomizer到SpringBoot2.x后WebServerFactoryCustomizer替代了EmbeddedServletContainerCustomizer。
java 代码解读复制代码@Configuration
public class LilithMvcConfig implements WebMvcConfigurer {


@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> customizer(){
    return factory -> {
        factory.setPort(8083);
    };
}

}

注释application.properties中的配置,重新启动该应用

图片

配置生效,tomcat在8083端口启动
这两种配置的优先级是怎样的?
在application.properties中配置
properties 代码解读复制代码server.port=8081
server.servlet.context-path=/servlet

保持WebServerFactoryCustomizer中配置的端口不变,重启应用

图片

控制台输出端口号为8083,路径为/servlet,可以确定重写WebServerFactoryCustomizer配置Tomcat的优先级更高,并且和application.properties是互补的。
二、Spring Boot中注册Servlet、Filter、Listener
在Java Web项目中,我们书写的Servlet、Filter、Listener组件都可以通过web.xml将这些组件配置到Tomcat容器中,那么在Spring Boot项目中使用内置Tomcat并且没有web.xml的情况如何注册这些组件?
Spring Boot为Java Web三大组件提供了通过XxxRegistrationBean的方式进行注册
ServletRegistrationBean注册Servlet
创建一个HalloServelet类继承HttpServlet类,并重写doGet()和doPost()方法
java 代码解读复制代码public class HalloServlet extends HttpServlet {

// 处理get请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doPost(req,resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    resp.getWriter().write("Hallo Servlet");
}

}

使用ServletRegisterBean,代替在web.xml中注册Servlet,在LilithMvcConfig中增加代码
java 代码解读复制代码// 注册Servlet
@Bean
public ServletRegistrationBean<HalloServlet> halloServletServletRegistrationBean(){

ServletRegistrationBean registrationBean = new ServletRegistrationBean();
registrationBean.setServlet(new HalloServlet());
registrationBean.setUrlMappings(Collections.singleton("/hallo"));
return registrationBean;

}

重启应用,在浏览器访问/hallo

图片

浏览器能够正常显示内容,自定义的Servlet成功注册。
FilterRegistrationBean注册Filter
首先自定义一个LilithFilter类实现Filter接口,在打印日志后放行请求
java 代码解读复制代码public class LilithFilter implements Filter {

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    System.out.println("LilithFilter running");
    chain.doFilter(request,response);
}

}

在LilithMvcConfig配置类中通过FilterRegistrationBean注册自定义的LilithFilter,并在其中设置过滤器要拦截的请求路径
java 代码解读复制代码// 注册Filter
@Bean
public FilterRegistrationBean<LilithFilter> lilithFilterFilterRegistrationBean(){

FilterRegistrationBean<LilithFilter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
filterFilterRegistrationBean.setFilter(new LilithFilter());
// 设置拦截的请求路径
filterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/hallo"));

return filterFilterRegistrationBean;

}

重启应用,在浏览器输入 /hallo

图片

控制台输出自定义的LilithFilter类中的日志信息,自定义的LilithFilter注册成功并生效
在controller包中新增一个HiController,增加一个hi()方法,请求映射路径为/hi
java 代码解读复制代码@Controller
public class HiController {

@RequestMapping("/hi")
@ResponseBody
public String hi(){
    return "Hi Lilith!";
}

}

重启应用,浏览器访问/hi,看是否会拦截

图片

控制台没有输出LilithFilter中设置的日志信息,说明没有被拦截,因为LilithFilter过滤器中没有设置拦截该请求,需要注意的是如果LilithFilter中没有设置拦截任何请求,默认拦截所有请求包括静态资源,但是不会jsp请求; /* 会拦截jsp请求。
ServletListenerRegistrationBean注册Listener
增加一个LilithListener类实现Listener接口,实现contextInitialized()方法和contextDestroyed()方法,并打印日志信
java 代码解读复制代码public class LilithListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent sce) {
    System.out.println("Web 应用启动了.......");
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
    System.out.println("Web 应用销毁了.......");
}

}

在LilithMvcConfig配置类中通过ServletListenerRegistrationBean注册自定义的监听器LilithListener
java 代码解读复制代码@Bean
public ServletListenerRegistrationBean<LilithListener> servletListenerRegistrationBean(){

ServletListenerRegistrationBean<LilithListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean<>();
servletListenerRegistrationBean.setListener(new LilithListener());
return servletListenerRegistrationBean;

}

重启应用,查看控制台输出的日志信息

图片

应用启动和关闭时都输出了自定义监听器LilithListener中定义的内容,说明自定义的监听器生效
Spring MVC的前端控制器DispatcherServlet就是通过这种方式来注册的,首先定义了DispatcherServlet类

图片

接着通过DispatcherServletRegistrationBean注册了DispatcherServlet

图片
转载来源:https://juejin.cn/post/7089854261105786910

关于Spring Boot:无法启动嵌入式Tomcat Servlet容器springboot启动不了tomcat的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于17. Spring Boot 配置嵌入式Servlet容器、4_7.springboot2.x嵌入式servlet容器自动配置原理、4、SpringBoot嵌入式Servlet容器、Spring Boot 2.x 嵌入式 Servlet 容器等相关内容,可以在本站寻找。

本文标签: