Servlet of microservice architecture day05 springboot

Posted by ngolehung84 on Sat, 22 Jan 2022 12:31:14 +0100

Old edition

Configure embedded Servlet container

  • Spring boot uses Tomcat as the embedded Servlet container by default
  • How to customize and modify Servlet container configuration 1. Customizing and modifying the configuration related to the Servlet container in the configuration file is essentially to modify the configuration using the default embedded Servlet container customizer of SpringBoot
Universal servlet Container configuration
server.xx=

Universal Tomcat to configure
server.tomcat.xx=

2. Write an embedded Servlet container customizer to modify the configuration of Servlet container In SpringBoot, there will be xxxCustomizer for extension configuration. Pay attention to learning!!

Register the three components of Servlet (Servlet, filter and listener)

  • Because SpringBoot starts the embedded Servlet container in the form of jar package by default to start the SpringBoot application, there is no web XML file
  • Three component registration methods: 1.ServletRegistrationBean
@Configuration
public class MyServerConfig {
    // Register Servlet components
    @Bean
    public ServletRegistrationBean myServlet(){
        ServletRegistrationBean servletRegistrationBean=new ServletRegistrationBean(new MyServlet(),"/my");
        return servletRegistrationBean;
    }
}

2.FilterRegistrationBean

	@Bean
    public FilterRegistrationBean myFilter(){
        FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new MyFilter());
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my"));
        return filterRegistrationBean;
    }

3.ServletListenerRegistrationBean

	@Bean
    public ServletListenerRegistrationBean myListener(){
        ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
        return registrationBean;
    }

When spring boot automatically configures spring MVC, it automatically registers the front-end controller of spring MVC: dispatcher servlet

    @Configuration
    @Conditional({DispatcherServletAutoConfiguration.DispatcherServletRegistrationCondition.class})
    @ConditionalOnClass({ServletRegistration.class})
    @EnableConfigurationProperties({WebMvcProperties.class})
    @Import({DispatcherServletAutoConfiguration.DispatcherServletConfiguration.class})
    protected static class DispatcherServletRegistrationConfiguration {
        private final WebMvcProperties webMvcProperties;
        private final MultipartConfigElement multipartConfig;

        public DispatcherServletRegistrationConfiguration(WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
            this.webMvcProperties = webMvcProperties;
            this.multipartConfig = (MultipartConfigElement)multipartConfigProvider.getIfAvailable();
        }

        @Bean(
            name = {"dispatcherServletRegistration"}
        )
        @ConditionalOnBean(
            value = {DispatcherServlet.class},
            name = {"dispatcherServlet"}
        )
        public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, this.webMvcProperties.getServlet().getPath());	//You can modify the request path intercepted by the spring MVC front-end controller by default by modifying the servlet path
            registration.setName("dispatcherServlet");
            registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());
            if (this.multipartConfig != null) {
                registration.setMultipartConfig(this.multipartConfig);
            }

            return registration;
        }
    }
  • "/": means to intercept all requests, including static resources, but not jsp requests, "/ *": means to intercept all requests, including jsp requests
  • There are several ways to inject beans:
    • @Bean annotation
    • Package scan:
      • @Controller
      • @Service
      • @Repository
      • @Component
    • @Import:
      • Class that implements the ImportSelector interface
      • Implements the ImportBeanDefinitionRegistry interface
    • Implement FactoryBean

SpringBoot supports other Servlet containers

  • Default support: Tomcat (default), Jetty,Undertow
    • Tomcat is the most stable server and is generally recommended
    • Jetty is more suitable for long connection services, but Netty is better than jetty
    • Undertow is more suitable for IO intensive servers or file servers than Tomcat
  • Jetty (long connection):
    	<dependency>
                    <artifactId>spring-boot-starter-jetty</artifactId>
                    <groupId>org.springframework.boot</groupId>
        </dependency>
  • Undertow (JSP not supported):
		<dependency>
            <artifactId>spring-boot-starter-undertow</artifactId>
            <groupId>org.springframework.boot</groupId>
        </dependency>

Automatic configuration principle of embedded Servlet container

  • Embeddedwebserverfactorycustomizer autoconfiguration: autoconfiguration of embedded containers
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties({ServerProperties.class})
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
    public EmbeddedWebServerFactoryCustomizerAutoConfiguration() {
    }

    @Configuration
    @ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})	// Judge whether Tomcat dependency is currently introduced
    public static class TomcatWebServerFactoryCustomizerConfiguration {
        public TomcatWebServerFactoryCustomizerConfiguration() {
        }
  • Take TomcatWebServerFactoryCustomizer as an example:
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::determineMaxHttpHeaderSize).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::getMaxHttpPostSize).asInt(DataSize::toBytes).when((maxHttpPostSize) -> {
            return maxHttpPostSize != 0;
        }).to((maxHttpPostSize) -> {
            this.customizeMaxHttpPostSize(factory, maxHttpPostSize);
        });
        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::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);
        });
        this.customizeStaticResources(factory);
        this.customizeErrorReportValve(properties.getError(), factory);
    }
  • How do changes to the embedded configuration container take effect 1.ServerProperties is also a Servlet container customizer 2. Modify the embedded Servlet container customizer Principle: BeanPostProcessorsRegistrar: import components into the container BeanPostProcessor: a post processor that performs initialization when the bean is initialized (the object is created and has not been assigned) Steps: 1.SpringBoot adds the corresponding embedded container factory to the container according to the dependency of the import 2. When a component in the container wants to create an object, it will call the post processor. As long as it is an embedded Servlet container factory, the post processor will work 3. The post processor obtains all embedded container processor customizers from the container and calls the methods in the embedded container processor customizer to configure the embedded container processor

Startup principle of embedded Servlet container

1.SpringBoot application startup run method 2.SpringBoot refreshcontext of IOC container (create IOC container object, initialize the container, and create each component in the container. If it is a web application, create AnnotationConfigEmbeddedWebApplicationContext; otherwise, create the default AnnotationConfigApplicationContext) 3. Refresh the created container refresh(context) 4. Call the overridden onRefresh() method at this time 5. The IOC container in the web will create an embedded Servlet container: createEmbeddedServletContainer() 6. Obtain the embedded Servlet container factory, and obtain the embedded Servlet container factory component from the IOC container. When the component exists, the Tomcat embedded Servlet container factory creates an object, and the post processor obtains all customizators to customize the configuration of the Tomcat embedded Servlet container 7. Use the Tomcat embedded servlet container factory to obtain the embedded servlet container 8. The embedded Servlet container creates objects and starts the Servlet container. First start the embedded Servlet container, and then get the objects in the IOC container At this point, the IOC container launch is complete and the embedded Servlet container is created

Using an external Servlet container

Embedded Servlet container:

  • Advantages: simple and convenient
  • Disadvantages: jsp is not supported by default, and the optimization and customization is complex (use the customization device [ServerProperties], and create the creation factory of embedded Servlet container by yourself)
  • External Servlet container: packaged by external installation of Tomcat application war package step 1. Create a war project and configure the Project Structure of the project 2. Specify the embedded Tomcat as provided 3. Write a subclass of SpringBootServletInitializer, call the configure method, and pass it into the SpringBoot application main program 4. Start the server to start the project Principle:
  • jar package: execute the main method of the SpringBoot main class, start the IOC container, and create an embedded Servlet container
  • war package: start the server. The server starts the SpringBoot application (SpringBootServletInitializer), and then the IOC container can be started
  • servlet3. Rules in 8.2.4 Shared libraries / runtimes pluggability of 0: 1. Server startup (web application startup) will create the ServletContainerInitializer instance in each jar package of the current web application 2. The implementation of ServletContainerInitializer is placed in the META-INF/services folder of the jar package, with a javax servlet. The file of ServletContainerInitializer, which contains the full class name of the implementation class of ServletContainerInitializer 3. You can use @ HandleTypes annotation to load the required classes when the application starts technological process: 1. Start Tomcat 2.
org\springframework\spring-web\5.1.9.RELEASE\spring-web-5.1.9.RELEASE.jar\META-INF\services\javax.servlet.ServletContainerInitializer

Spring's web module has this file: org springframework. web. SpringServletContainerInitializer 3.SpringServletContainerInitializer passes all types of classes marked by @ HandleTypes({WebApplicationInitializer.class}) into set < class <? > > of onStartup method Create an instance of a class of type WebApplicationInitializer 4. Each WebApplicationInitializer calls its own onStartup method to start 5. The springbootservletinitializer class will create an object and execute the onStartup method to start 6. The onStartup method executed by springbootservletinitializer will call createRootApplicationContext to create a container

protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
		// Create spring application builder builder
        SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
        builder.main(this.getClass());
        ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
        if (parent != null) {
            this.logger.info("Root context already created (using as parent).");
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
            builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
        }

        builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
        builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
        // Call the configure method, and the subclass overrides the method to pass in the main program class of SpringBoot
        builder = this.configure(builder);	
        builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)});
        // Using builder to create a Spring application
        SpringApplication application = builder.build();
        if (application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
            application.addPrimarySources(Collections.singleton(this.getClass()));
        }

        Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
        if (this.registerErrorPageFilter) {
            application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
        }
		// Launch Spring application
        return this.run(application);
    }

7.Spring starts successfully and creates an IOC container

	protected WebApplicationContext run(SpringApplication application) {
        return (WebApplicationContext)application.run(new String[0]);
    }
  • Start the Servlet container first, and then start the SpringBoot application