preface

In Spring Boot, we learned how to quickly launch a Spring Boot application in JAR form. Spring Boot also supports traditional deployment methods: Package the project into a WAR, and then load and start it by the Web server. Using Tomcat as an example, we will quickly learn how to deploy a Spring Boot project in WAR mode, hosted on Github, and do some simple source code analysis.

The body of the

Use the Spring Initializr tool to download the basic Spring Boot project and choose Maven to build with the official version 1.5.16. Select only one Web dependency.

inheritanceSpringBootServletInitializerloading

Open after download the project, to start the class SpringbootTomcatApplication modifications, Inheritance SpringBootServletInitializer this abstract class, and rewrite the superclass method SpringApplicationBuilder configure (SpringApplicationBuilder builder).

@SpringBootApplication
public class SpringbootTomcatApplication extends SpringBootServletInitializer  {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
	    return builder.sources(SpringbootTomcatApplication.class);
	}

	public static void main(String[] args) { SpringApplication.run(SpringbootTomcatApplication.class, args); }}Copy the code

SpringBootServletInitializer class will start in the Servlet container program allows us to custom configuration program, and here we will need to make the Servlet container loading this class when start the program.

Example Change the packaging mode to WAR

Next, in the POM.xml file, change the packaging to WAR so that Maven builds as WAR.

<groupId>com.one</groupId>
<artifactId>springboot-tomcat</artifactId>
<version>0.0.1 - the SNAPSHOT</version>
<packaging>war</packaging>
Copy the code

Another note: To ensure that the embedded servlet container does not affect the servlet container where the WAR file is deployed, this is Tomcat. We also need to mark the dependency of the embedded servlet container as provided.

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

Implement Rest request processing

To verify that the WAR deployment was successful, we implemented a basic function to handle Web requests, adding some Spring MVC code to the startup class

@SpringBootApplication
@RestController
public class SpringbootTomcatApplication extends SpringBootServletInitializer  {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
	    return builder.sources(SpringbootTomcatApplication.class);
	}

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


	@RequestMapping(value = "/")
	public String hello(a) {
		return "hello tomcat"; }}Copy the code

Project package

Now you can package the Spring Boot program as a WAR and have the Tomcat server load it, using the build command in the current project path

mvn clean package
Copy the code

If BUILD SUCCESS appears, the packaging is successful

You can then see the generated WAR in the project’s Target directory.

To deploy Tomcat

Springboot -tomcat-0.0.1- snapshot. war in the tomcat folder **webapps** and run tomcat. Start-up success can enter http://localhost:8080/springboot-tomcat-0.0.1-SNAPSHOT/, the browser requests the simple Web application.

At this point, the Spring Boot program deployed in WAR mode is complete. 🎉🎉🎉

Source code analysis

Done here, can not help but have a question: why is inherited SpringBootServletInitializer classes, and overwrite the configure method can go deployed in war? With the question, we look for the answer from the source point of view.

At startup class SpringbootTomcatApplication override a method for breakpoints, look at the Tomcat run project this method invocation process.

Through the Debug mode to run projects, when running to this line of code, you can see two important classes SpringBootServletInitializer and SpringServletContainerInitializer.

From figure you can see the configure method invocation is in the parent class createRootApplicationContext, specific code is as follows, non-critical part has been omitted, important commented out.

protected WebApplicationContext createRootApplicationContext( ServletContext servletContext) {
		SpringApplicationBuilder builder = createSpringApplicationBuilder(); // Create a New Builder to build the SpringApplication instance
		builder.main(getClass());
		/ /...
		builder.initializers(
				new ServletContextApplicationContextInitializer(servletContext));
		builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
		builder = configure(builder); // Call the subclass method to configure the current Builder
		builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
		SpringApplication application = builder.build(); // Build the SpringApplication instance
		if(application.getSources().isEmpty() && AnnotationUtils .findAnnotation(getClass(), Configuration.class) ! =null) {
			application.getSources().add(getClass());
		}
    	/ /...
		return run(application);  // Run the SpringApplication instance
	}
Copy the code

The SpringApplication builder instance should follow the builder design pattern to complete the build assembly of the SpringApplication.

CreateRootApplicationContext method calls or done in this class, this is familiar with, because the traditional Spring Web will also create a WebApplicationContext instance of project startup.

@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		// Logger initialization is deferred in case a ordered
		// LogServletContextInitializer is being used
		this.logger = LogFactory.getLog(getClass());
		WebApplicationContext rootAppContext = createRootApplicationContext(
				servletContext); // Create a WebApplicationContext instance.
        // ...
	}
Copy the code

How is the onStartup method implemented? SpringServletContainerInitializer class appearance.

SpringServletContainerInitializer ServletContainerInitializer interface class implements the Servlet 3.0 specification, Means that when the Servlet container start, to invoke ServletContainerInitializer onStartup method of interface notification class implements this interface.

public interface ServletContainerInitializer {
    void onStartup(Set
       
        > c, ServletContext ctx)
       > throws ServletException;
}
Copy the code

Now we’ll look at SpringServletContainerInitializer onStarup method implementation is as follows, 23 ~ 24 key code line initializers is a LinkedList collection, have all realize WebApplicationInitializer interface instance, iterate over here will call their respective onStartup method The ServletContext instance is used to complete the Web server startup notification.

@Override
public void onStartup(Set
       
        > webAppInitializerClasses, ServletContext servletContext)
       >
      throws ServletException {
   List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
   if(webAppInitializerClasses ! =null) {
      for(Class<? > waiClass : webAppInitializerClasses) {if(! waiClass.isInterface() && ! Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {try {
                / / extraction webAppInitializerClasses collection of WebApplicationInitializer interface in the instance
               initializers.add((WebApplicationInitializer) waiClass.newInstance());
            }
            catch (Throwable ex) {
               throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); }}}}// ...
    
   for (WebApplicationInitializer initializer : initializers) {
      initializer.onStartup(servletContext); / / call all realize WebApplicationInitializer instance onStartup method}}Copy the code

To the track to perform SpringServletContainerInitializer class 22, We can see that the collection contains our startup class, so we end up calling its parent’s onStartup method to create an instance of WebApplicationContext.

See here, we summarize these several class call process, comb the Spring Boot program WAR startup process:

SpringServletContainerInitializer#onStartup

​ => SpringBootServletInitializer#onStartup

​ => “SpringBootServletInitializer#createRootApplicationContext​ =>SpringbootTomcatApplication#configure`

In addition, I also harvest is: when performing SpringBootServletInitializer createRootApplicationContext method finally, call the run (application).

This also indicates that when deploying Spring Boot projects in WAR mode, the fixed generated Main method is no longer executed and can be removed.

// This method is useless code when the project is deployed as WAR
public static void main(String[] args) {
	SpringApplication.run(SpringbootTomcatApplication.class, args);
}
Copy the code

conclusion

This article is mainly to learn how to make Spring Boot in WAR mode, and a simple source analysis, to help us better understand Spring Boot. Hope to help, there will be more actual combat and analysis, please look forward to ha. 😁😁😁.

reference