springboot10-servlet自定義配置

嵌入式servlet配置修改

SpringBoot默認使用Tomcat作爲嵌入式的Servlet容器

在這裏插入圖片描述

嵌入式tomcat:tomca-embed-core

問題?

1)、如何定製和修改Servlet容器相關配置;

​ 1.修改和server有關的配置:(ServerProperties.class)

server.port=8081
server.servlet.context-path=/ogj
server.tomcat.uri-encoding=UTF-8

//通用的Server配置
server.xxx
//Tomcat的設置
server.tomcat.xxx

​ 前幾個版本還可以自定義接口設置,但是在springboot2.x以後取消了該接口,所有Servlet配置在配置文件中配置。

對於SpringBoot中可以用xxxCustomize進行定製配置。

2. 註冊Servlet三大組件【Servlet、Filter、Listener】

由於SpringBoot默認是以jar包的方式啓動嵌入式的Servlet容器來啓動SpringBoot的web應用,沒有web.xml文件。

所以SpringBoot使用三大組件來配置web:Servlet、Filter、Listener**

註冊三大組件的方式:

1、ServletRegistrationBean

定義servlet

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----------doGet----------------");
        //此處不能訪問父方法,否則會405
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----------doPost----------------");
        resp.getWriter().write("Hello MyServlet");
    }
}

註冊servlet:

@Bean
public ServletRegistrationBean myServlet(){
    ServletRegistrationBean registrationBean =
            new ServletRegistrationBean(new MyServlet(), "/myServlet");
    return registrationBean;
}

2、FilterRegistrationBean

定義Filter

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("-----Filter-----");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

註冊Filter

//註冊Filter 攔截URL
    @Bean
    public FilterRegistrationBean myFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new MyFilter());
        registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
        return registrationBean;
    }

3、ServletListenerRegistrationBean

定義Listener:

public class Mylistener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("contextInitialized...web應用啓動");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("contextDestroy...web項目銷燬");
    }
}

註冊Listener:

//註冊Listener 監聽器
    @Bean
    public ServletListenerRegistrationBean<Mylistener> myListener(){
        ServletListenerRegistrationBean<Mylistener> registrationBean
                = new ServletListenerRegistrationBean<>(new Mylistener());
        return registrationBean;

    }

SpringBoot幫助我們自動SpringMVC的時候,自動的註冊SpringMVC的前端控制器:DispatcherServlet;

配置類:DispatcherServletAutoConfiguration

ctrl+n查找該類源碼可得自動註冊方法:dispatcherServletRegistration

@Bean(
            name = {"dispatcherServletRegistration"}
        )
        @ConditionalOnBean(
            value = {DispatcherServlet.class},
            name = {"dispatcherServlet"}
        )
        public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
            //默認攔截: / 所有請求;包括靜態資源,但是不攔截jsp請求,  /* 會攔截jsp
            //可以通過server.servletpath來修改SpringMVC前端控制器攔截的請求路徑
           
            registration.setName("dispatcherServlet");
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }

在這裏插入圖片描述

在這裏插入圖片描述

  • 默認攔截: / 所有請求; 包括靜態資源,但是不攔截jsp請求, /* 會攔截jsp

  • 可以通過server.servletpath來修改SpringMVC前端控制器攔截的請求路徑

2)、SpringBoot能不能支持其他的Servlet容器;

3)、替換爲其他嵌入式Servlet容器

默認支持:Tomcat、Jetty、Undertow,新版本SpringBoot中還添加了Netty新機制容器;默認使用tomcat

在這裏插入圖片描述

<!--引入web模塊-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
<!--引入web模塊的時候就是默認使用的tomcat作爲Servlet容器-->

切換方式:

​ 1、在pow中排除tomcat的依賴;

​ 2、添加jetty(Undertow)的依賴; Undertow不支持jsp組件;

<!--引入web模塊-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>

    <!--配置其他Servlet容器-->
    <dependency>
        <artifactId>spring-boot-starter-jetty</artifactId>
        <groupId>org.springframework.boot</groupId>
    </dependency>

4)、嵌入式Servlet容器自動配置原理:

新版本SpringBoot2.x在該類中進行自動配置:EmbeddedWebServerFactoryCustomizerAutoConfiguration

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication
@EnableConfigurationProperties({ServerProperties.class})
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
    //配置NettyWebServer
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({HttpServer.class})
    public static class NettyWebServerFactoryCustomizerConfiguration {
        public NettyWebServerFactoryCustomizerConfiguration() {
        }

        @Bean
        public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
            return new NettyWebServerFactoryCustomizer(environment, serverProperties);
        }
    }
    //配置Undertow
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Undertow.class, SslClientAuthMode.class})
    public static class UndertowWebServerFactoryCustomizerConfiguration {
        public UndertowWebServerFactoryCustomizerConfiguration() {
        }

        @Bean
        public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
            return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
        }
    }
    //配置jetty
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Server.class, Loader.class, WebAppContext.class})
    public static class JettyWebServerFactoryCustomizerConfiguration {
        public JettyWebServerFactoryCustomizerConfiguration() {
        }

        @Bean
        public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
            return new JettyWebServerFactoryCustomizer(environment, serverProperties);
        }
    }
    //配置Tomcat
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
    public static class TomcatWebServerFactoryCustomizerConfiguration {
        public TomcatWebServerFactoryCustomizerConfiguration() {
        }

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

新版本的SpringBoot2.x使用的自定義配置已經不是傳入容器了,而是傳入xxWebServerFactoryCustomizer,作爲自定義Servlet容器配置。

我們還可以看到以下幾個xxWebServletFactoryCustomizer配置類

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-EMFErJI6-1583925810401)(C:\Users\ouguangji\AppData\Roaming\Typora\typora-user-images\image-20200311090632948.png)]

Tomcat配置過程(該代碼位於源碼:TomcatWebServerFactoryCustomizer):

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

//customer的自動配置
public void customize(ConfigurableTomcatWebServerFactory factory) {
        ServerProperties properties = this.serverProperties;
        Tomcat tomcatProperties = properties.getTomcat();
        PropertyMapper propertyMapper = PropertyMapper.get();
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds).as(Long::intValue).to(factory::setBackgroundProcessorDelay);
        this.customizeRemoteIpValve(factory);
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive).to((maxThreads) -> {
            this.customizeMaxThreads(factory, tomcatProperties.getMaxThreads());
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive).to((minSpareThreads) -> {
            this.customizeMinThreads(factory, minSpareThreads);
        });
        propertyMapper.from(this.serverProperties.getMaxHttpHeaderSize()).whenNonNull().asInt(DataSize::toBytes).when(this::isPositive).to((maxHttpHeaderSize) -> {
            this.customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull().asInt(DataSize::toBytes).to((maxSwallowSize) -> {
            this.customizeMaxSwallowSize(factory, maxSwallowSize);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getMaxHttpFormPostSize).asInt(DataSize::toBytes).when((maxHttpFormPostSize) -> {
            return maxHttpFormPostSize != 0;
        }).to((maxHttpFormPostSize) -> {
            this.customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getAccesslog).when(Accesslog::isEnabled).to((enabled) -> {
            this.customizeAccessLog(factory);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
        properties.getClass();
        propertyMapper.from(properties::getConnectionTimeout).whenNonNull().to((connectionTimeout) -> {
            this.customizeConnectionTimeout(factory, connectionTimeout);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getConnectionTimeout).whenNonNull().to((connectionTimeout) -> {
            this.customizeConnectionTimeout(factory, connectionTimeout);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive).to((maxConnections) -> {
            this.customizeMaxConnections(factory, maxConnections);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive).to((acceptCount) -> {
            this.customizeAcceptCount(factory, acceptCount);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getProcessorCache).to((processorCache) -> {
            this.customizeProcessorCache(factory, processorCache);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getRelaxedPathChars).as(this::joinCharacters).whenHasText().to((relaxedChars) -> {
            this.customizeRelaxedPathChars(factory, relaxedChars);
        });
        tomcatProperties.getClass();
        propertyMapper.from(tomcatProperties::getRelaxedQueryChars).as(this::joinCharacters).whenHasText().to((relaxedChars) -> {
            this.customizeRelaxedQueryChars(factory, relaxedChars);
        });
        this.customizeStaticResources(factory);
        this.customizeErrorReportValve(properties.getError(), factory);
    }

由於Servlet加載和配置原理在SpringBoot2.X重構,這就不探究了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章