Using external Servlet container in SpringBoot

Posted by halfman on Tue, 04 Feb 2020 18:49:04 +0100

1, Comparison between the spring boot embedded Servlet container and the external Servlet container

  • The embedded Servlet container application packs the program as Jar package, and the external Servlet container application packs the program as War package.
  • Advantages of embedded Servlet container: simple and portable. Disadvantages: JSP is not supported by default, and optimization and customization are complex.

  how to optimize and customize the embedded Servlet container:

  ① using the customizer: ServerProperties, customizing EmbeddedServletContainerCustomizer
  ② create factory of embedded Servlet container by yourself [embedded Servlet container factory]

2, How to use an external Servlet container

1. Create a War package project

; File New Project, select Spring Initializr, click Next to enter the following page, change the default Jar package to War, and then Next until Finish.

Created directory structure:

   since the War package project is created, the above directory structure is not complete. We need to manually create web.xml and webapp. There are two ways to create:

① Manually create the webapp directory under src/main and the web.xml file under / src/main/webapp/WEB-INF.

② Improve the directory structure in Project Structure of IDEA.

Click Module, select web, and come to the following page:

Double click the url circled above to open the following dialog box:

Click OK and select Yes.

On the Project Structure page, click "+" as shown in the figure below to open the following dialog box, modify the directory / src/main/webapp that generates the web.xml file, and then click OK.

After clicking OK in the previous step, click Apply, and then select OK to create the webapp directory and web.xml file.

The improved directory structure is as follows:

2. Specify embedded tomcat as provided

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

3. You must write an implementation subclass of SpringBootServletInitializer.

The purpose of     is to call the configure method and pass in the main program of the springboot application.

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(SpringBootWebJspApplication.class);
    }
}

4. Start server

3, How external servlets start SpringBoot applications (1.5.x)

3, principle

  • Start the project as a jar package: execute the main method of the spring boot main class, start the IOC container, and create an embedded Servlet container.
  • Start the project as a war package: start the server, start the SpringBoot application [SpringBootServletInitializer], and then start the IOC container.

   in 8.2.4 Shared libraries / runtimes pluggability of spring 3.0 (spring annotation version), there are the following rules:

[1] When the server (web application) starts, the ServletContainerInitializer instance in each jar package in the current web application will be created.
[2] The implementation of the ServletContainerInitializer is placed in the META-INF/services folder of the jar package. There must be a file named javax.servlet.ServletContainerInitializer, whose content is the full class name of the implementation class of the ServletContainerInitializer.
[3] You can also use @ HandlesTypes annotation to load the classes we are interested in when the application starts.

Steps:

[1] Start tomcat server;
[2] In the.. / org/springframework/spring-web/4.3.14.RELEASE/spring-web-4.3.14.RELEASE.jar/META-INF/services directory, there is a file named javax.servlet.ServletContainerInitializer. The file content is:

org.springframework.web.SpringServletContainerInitializer

It is equivalent to starting the SpringServletContainerInitializer when the web application starts.
[3] SpringServletContainerInitializer will pass all types of classes (classes of interest) annotated by @ HandlesTypes(WebApplicationInitializer.class) into the set < class <? > > webappinitializerclasses of onStartup method to create instances of these WebApplicationInitializer type classes (not interfaces, not abstract classes).
[4] Each WebApplicationInitializer calls its own onStartup, which is equivalent to that our SpringBootServletInitializer class will be created and the onStartup method will be executed. WebApplicationInitializer is an interface. Its implementation class is as follows:

//Set < class <? > > webappinitializerclasses indicates the class of interest
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
    List<WebApplicationInitializer> initializers = new LinkedList();
    Iterator var4;
    if (webAppInitializerClasses != null) {
       var4 = webAppInitializerClasses.iterator();

    while(var4.hasNext()) {
       Class<?> waiClass = (Class)var4.next();
       //If the interface or abstract class does not die
       if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
          try {
              //Add to the collection of WebApplicationInitializer objects
                initializers.add((WebApplicationInitializer)waiClass.newInstance());
              } catch (Throwable var7) {
                  throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
              }
            }
          }
       }
       
     if (initializers.isEmpty()) {
        servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
     } else {
          servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
          AnnotationAwareOrderComparator.sort(initializers);
          var4 = initializers.iterator();

          while(var4.hasNext()) {
              WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
              //Each WebApplicationInitializer object calls its own onStartup method to create a SpringBootServletInitializer object
              initializer.onStartup(servletContext);
          }
      }
}
public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(SpringBootWebJspApplication.class);
    }

}

[5] When the SpringBootServletInitializer instance executes the onStartup method, it will call the createRootApplicationContext method to create the container

public void onStartup(ServletContext servletContext) throws ServletException {
        this.logger = LogFactory.getLog(this.getClass());
        WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
        if (rootAppContext != null) {
            servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                public void contextInitialized(ServletContextEvent event) {
                }
            });
        } else {
            this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
        }

    }

protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
//1. Create SpringApplicationBuilder object
    SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
    StandardServletEnvironment environment = new StandardServletEnvironment();
    environment.initPropertySources(servletContext, (ServletConfig)null);
    builder.environment(environment);
    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(AnnotationConfigEmbeddedWebApplicationContext.class);
      //Call the configure method, override the method in the subclass, and pass in the main program class of SpringBoot
      builder = this.configure(builder);
      //Using builder to create spring application
      SpringApplication application = builder.build();
      if (application.getSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
          application.getSources().add(this.getClass());
      }

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

protected SpringApplicationBuilder createSpringApplicationBuilder() {
    return new SpringApplicationBuilder(new Object[0]);
}

[6] Launch spring app and create IOC container

 public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            new FailureAnalyzers(context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //Refresh IOC container
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            listeners.finished(context, (Throwable)null);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
            throw new IllegalStateException(var9);
        }
    }

One word summary: start the Servlet container, start the spring application, and then create the IOC container.

72 original articles published, praised 4, visits 2495
Private letter follow

Topics: Spring xml SpringBoot Tomcat