A spring boot web application can be packaged in a jar file or can be packaged in a war file as we saw in this tutorial. With both options, an embedded servlet container (tomcat by default) can also be included in the package so that the archive (jar or war) can be executed as a standalone application. That is only possible when we use spring-boot maven plugin.
In this tutorial we are first going to create a traditional maven war file and deploy it to the tomcat server, then we will create an executable war file with spring-boot plugin, deploy it to the server and execute as well. We will also see the difference between the two methods and will analyze war file structure created by spring-boot maven plugin.
Creating an example web project
@SpringBootApplication
public class WarStructureExample extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure (SpringApplicationBuilder builder) {
return builder.sources(WarStructureExample.class);
}
public static void main (String[] args) {
SpringApplication.run(WarStructureExample.class, args);
}
@Controller
public static class MyController {
@RequestMapping("/")
public String handler (Model model) {
model.addAttribute("msg",
"a war structure example");
return "myPage";
}
}
}
Note that we implemented SpringBootServletInitializer, in case if you are not aware of it's purpose please check it out here.
src/main/webapp/WEB-INF/pages/myPage.jsp
<h2>From JSP page </h2>
<%@ page language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<body>
Message : ${msg}
</body>
</html>
src/main/resources/application.properties
spring.mvc.view.prefix= /WEB-INF/pages/
spring.mvc.view.suffix= .jsp
Traditional packaging and deployment
If we want to create a war file by maven normal package goal instead of spring-boot plugin, then we won't need spring-boot plugin. This war file of course won't be executable, but can be deployed to a server running with servlet container.
pom.xml
<project ....>
<modelVersion>4.0.0</modelVersion>
<groupId>com.logicbig.example</groupId>
<artifactId>traditional-war</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
spring-boot-starter-parent's pom.xml configures maven-war-plugin by default, we just needed <packaging>war</packaging>.
Also the embedded servlet container which comes with spring-boot-starter-web, may interfere with the servlet container to which the war file will be deployed. To avoid that we needed to override the default dependency scope to 'provided', so that embedded tomcat won't be included in the final war file.
Packaging the war file
D:\examples\traditional-war>mvn -q clean
D:\examples\traditional-war>tree /A /F
Folder PATH listing for volume Data
Volume serial number is 00000051 68F9:EDFA
D:.
| pom.xml
|
\---src
\---main
+---java
| \---com
| \---logicbig
| \---example
| WarStructureExample.java
|
+---resources
| application.properties
|
\---webapp
\---WEB-INF
\---pages
myPage.jsp
D:\examples\traditional-war>mvn -q package
D:\examples\traditional-war>tree /A /F
Folder PATH listing for volume Data
Volume serial number is 00000024 68F9:EDFA
D:.
| pom.xml
|
+---src
| \---main
| +---java
| | \---com
| | \---logicbig
| | \---example
| | WarStructureExample.java
| |
| +---resources
| | application.properties
| |
| \---webapp
| \---WEB-INF
| \---pages
| myPage.jsp
|
\---target
| traditional-war-1.0-SNAPSHOT.war
|
+---classes
| | application.properties
| |
| \---com
| \---logicbig
| \---example
| WarStructureExample$MyController.class
| WarStructureExample.class
|
+---generated-sources
| \---annotations
+---maven-archiver
| pom.properties
|
+---maven-status
| \---maven-compiler-plugin
| \---compile
| \---default-compile
| createdFiles.lst
| inputFiles.lst
|
\---traditional-war-1.0-SNAPSHOT
+---META-INF
\---WEB-INF
+---classes
| | application.properties
| |
| \---com
| \---logicbig
| \---example
| WarStructureExample$MyController.class
| WarStructureExample.class
|
+---lib
| classmate-1.3.3.jar
| hibernate-validator-5.2.4.Final.jar
| jackson-annotations-2.8.5.jar
| jackson-core-2.8.5.jar
| jackson-databind-2.8.5.jar
| jboss-logging-3.3.0.Final.jar
| jcl-over-slf4j-1.7.22.jar
| jul-to-slf4j-1.7.22.jar
| log4j-over-slf4j-1.7.22.jar
| logback-classic-1.1.8.jar
| logback-core-1.1.8.jar
| slf4j-api-1.7.22.jar
| snakeyaml-1.17.jar
| spring-aop-4.3.5.RELEASE.jar
| spring-beans-4.3.5.RELEASE.jar
| spring-boot-1.4.3.RELEASE.jar
| spring-boot-autoconfigure-1.4.3.RELEASE.jar
| spring-boot-starter-1.4.3.RELEASE.jar
| spring-boot-starter-logging-1.4.3.RELEASE.jar
| spring-boot-starter-web-1.4.3.RELEASE.jar
| spring-context-4.3.5.RELEASE.jar
| spring-core-4.3.5.RELEASE.jar
| spring-expression-4.3.5.RELEASE.jar
| spring-web-4.3.5.RELEASE.jar
| spring-webmvc-4.3.5.RELEASE.jar
| validation-api-1.1.0.Final.jar
|
\---pages
myPage.jsp
D:\examples\traditional-war>
Deploying the war in tomcat server
I am using tomcat 9.0.0.M15 for this example:
(1)Start tomcat server by clicking on <tomcat-home>/bin/startup.bat
(2)Go to the main tomcat page at localhost:8080 and click on 'Manager App' button. If it asks for user/password then enter it (a username/password for the role "manager-gui" should be setup in <tomcat-home>/conf/tomcat-users.xml). Now you will be on 'Tomcat web Application Manager' page.
(3)'Choose File' under 'WAR file to deploy' section (near bottom), there you have to select your war file created under the project's target folder. Click on 'Deploy' button. This will deploy our war to the server.
(4)In 'Applications' table (near top) find '/traditional-war-1.0-SNAPSHOT' and click on that, you will see the expected output:
Also check out step by step related tutorial to learn how to install and deploy wars on Tomcat server.
Packaging war using spring-boot plugin
We are going to use same main class as above. There will be some important changes in the pom.xml file.
pom.xml
Here we are going to add spring-boot maven plugin:
<project ..........>
<modelVersion>4.0.0</modelVersion>
<groupId>com.logicbig.example</groupId>
<artifactId>boot-war</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.4.3.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Note that we also needed to add additional tomcat-embed-jasper (a Core Tomcat implementation) dependency. This is needed only when we run the executable war.
Packaging the war file
D:\examples\boot-war>mvn -q clean
D:\examples\boot-war>tree /A /F
Folder PATH listing for volume Data
Volume serial number is 000000F6 68F9:EDFA
D:.
| pom.xml
|
\---src
\---main
+---java
| \---com
| \---logicbig
| \---example
| WarStructureExample.java
|
+---resources
| application.properties
|
\---webapp
\---WEB-INF
\---pages
myPage.jsp
D:\examples\boot-war>mvn -q package
D:\examples\boot-war>tree /A /F
Folder PATH listing for volume Data
Volume serial number is 00000074 68F9:EDFA
D:.
| pom.xml
|
+---src
| \---main
| +---java
| | \---com
| | \---logicbig
| | \---example
| | WarStructureExample.java
| |
| +---resources
| | application.properties
| |
| \---webapp
| \---WEB-INF
| \---pages
| myPage.jsp
|
\---target
| boot-war-1.0-SNAPSHOT.war
| boot-war-1.0-SNAPSHOT.war.original
|
+---boot-war-1.0-SNAPSHOT
| +---META-INF
| \---WEB-INF
| +---classes
| | | application.properties
| | |
| | \---com
| | \---logicbig
| | \---example
| | WarStructureExample$MyController.class
| | WarStructureExample.class
| |
| +---lib
| | classmate-1.3.3.jar
| | hibernate-validator-5.2.4.Final.jar
| | jackson-annotations-2.8.5.jar
| | jackson-core-2.8.5.jar
| | jackson-databind-2.8.5.jar
| | jboss-logging-3.3.0.Final.jar
| | jcl-over-slf4j-1.7.22.jar
| | jul-to-slf4j-1.7.22.jar
| | log4j-over-slf4j-1.7.22.jar
| | logback-classic-1.1.8.jar
| | logback-core-1.1.8.jar
| | slf4j-api-1.7.22.jar
| | snakeyaml-1.17.jar
| | spring-aop-4.3.5.RELEASE.jar
| | spring-beans-4.3.5.RELEASE.jar
| | spring-boot-1.4.3.RELEASE.jar
| | spring-boot-autoconfigure-1.4.3.RELEASE.jar
| | spring-boot-starter-1.4.3.RELEASE.jar
| | spring-boot-starter-logging-1.4.3.RELEASE.jar
| | spring-boot-starter-web-1.4.3.RELEASE.jar
| | spring-context-4.3.5.RELEASE.jar
| | spring-core-4.3.5.RELEASE.jar
| | spring-expression-4.3.5.RELEASE.jar
| | spring-web-4.3.5.RELEASE.jar
| | spring-webmvc-4.3.5.RELEASE.jar
| | validation-api-1.1.0.Final.jar
| |
| \---pages
| myPage.jsp
|
+---classes
| | application.properties
| |
| \---com
| \---logicbig
| \---example
| WarStructureExample$MyController.class
| WarStructureExample.class
|
+---generated-sources
| \---annotations
+---maven-archiver
| pom.properties
|
\---maven-status
\---maven-compiler-plugin
\---compile
\---default-compile
createdFiles.lst
inputFiles.lst
D:\examples\boot-war>
The content under boot-war-1.0-SNAPSHOT folder are original war file content.
To see the war file enhanced by spring-boot plugin, we have to extract boot-war-1.0-SNAPSHOT.war:
D:\examples\boot-war>md temp
D:\examples\boot-war>cd temp
D:\examples\boot-war\temp>jar -xf ..\target\boot-war-1.0-SNAPSHOT.war
D:\examples\boot-war\temp>tree /A /F
Folder PATH listing for volume Data
Volume serial number is 000000B9 68F9:EDFA
D:.
+---META-INF
| | MANIFEST.MF
| |
| \---maven
| \---com.logicbig.example
| \---boot-war
| pom.properties
| pom.xml
|
+---org
| \---springframework
| \---boot
| \---loader
| | ExecutableArchiveLauncher$1.class
| | ExecutableArchiveLauncher.class
| | JarLauncher.class
| | LaunchedURLClassLoader$1.class
| | LaunchedURLClassLoader.class
| | Launcher.class
| | MainMethodRunner.class
| | PropertiesLauncher$1.class
| | PropertiesLauncher$ArchiveEntryFilter.class
| | PropertiesLauncher$FilteredArchive$1.class
| | PropertiesLauncher$FilteredArchive.class
| | PropertiesLauncher$PrefixMatchingArchiveFilter.class
| | PropertiesLauncher.class
| | WarLauncher.class
| |
| +---archive
| | Archive$Entry.class
| | Archive$EntryFilter.class
| | Archive.class
| | ExplodedArchive$1.class
| | ExplodedArchive$FileEntry.class
| | ExplodedArchive$FileEntryIterator$EntryComparator.class
| | ExplodedArchive$FileEntryIterator.class
| | ExplodedArchive.class
| | JarFileArchive$EntryIterator.class
| | JarFileArchive$JarFileEntry.class
| | JarFileArchive.class
| |
| +---data
| | ByteArrayRandomAccessData.class
| | RandomAccessData$ResourceAccess.class
| | RandomAccessData.class
| | RandomAccessDataFile$DataInputStream.class
| | RandomAccessDataFile$FilePool.class
| | RandomAccessDataFile.class
| |
| +---jar
| | AsciiBytes.class
| | Bytes.class
| | CentralDirectoryEndRecord.class
| | CentralDirectoryFileHeader.class
| | CentralDirectoryParser.class
| | CentralDirectoryVisitor.class
| | FileHeader.class
| | Handler.class
| | JarEntry.class
| | JarEntryFilter.class
| | JarFile$1.class
| | JarFile$2.class
| | JarFile$3.class
| | JarFile$JarFileType.class
| | JarFile.class
| | JarFileEntries$1.class
| | JarFileEntries$EntryIterator.class
| | JarFileEntries.class
| | JarURLConnection$1.class
| | JarURLConnection$JarEntryName.class
| | JarURLConnection.class
| | ZipInflaterInputStream.class
| |
| \---util
| SystemPropertyUtils.class
|
\---WEB-INF
+---classes
| | application.properties
| |
| \---com
| \---logicbig
| \---example
| WarStructureExample$MyController.class
| WarStructureExample.class
|
+---lib
| classmate-1.3.3.jar
| hibernate-validator-5.2.4.Final.jar
| jackson-annotations-2.8.5.jar
| jackson-core-2.8.5.jar
| jackson-databind-2.8.5.jar
| jboss-logging-3.3.0.Final.jar
| jcl-over-slf4j-1.7.22.jar
| jul-to-slf4j-1.7.22.jar
| log4j-over-slf4j-1.7.22.jar
| logback-classic-1.1.8.jar
| logback-core-1.1.8.jar
| slf4j-api-1.7.22.jar
| snakeyaml-1.17.jar
| spring-aop-4.3.5.RELEASE.jar
| spring-beans-4.3.5.RELEASE.jar
| spring-boot-1.4.3.RELEASE.jar
| spring-boot-autoconfigure-1.4.3.RELEASE.jar
| spring-boot-starter-1.4.3.RELEASE.jar
| spring-boot-starter-logging-1.4.3.RELEASE.jar
| spring-boot-starter-web-1.4.3.RELEASE.jar
| spring-context-4.3.5.RELEASE.jar
| spring-core-4.3.5.RELEASE.jar
| spring-expression-4.3.5.RELEASE.jar
| spring-web-4.3.5.RELEASE.jar
| spring-webmvc-4.3.5.RELEASE.jar
| validation-api-1.1.0.Final.jar
|
+---lib-provided
| ecj-4.5.1.jar
| spring-boot-starter-tomcat-1.4.3.RELEASE.jar
| tomcat-embed-core-8.5.6.jar
| tomcat-embed-el-8.5.6.jar
| tomcat-embed-jasper-8.5.6.jar
| tomcat-embed-websocket-8.5.6.jar
|
\---pages
myPage.jsp
D:\examples\boot-war\temp>
In above output, followings things are noticeable
- 'provided' dependencies are placed in 'WEB-INF/lib-provided'. This is to avoid any clash when the war is deployed in a servlet container.
- 'WEB-INF/lib-provided' jars will only be used when war is executed as an application and the embedded server is used. We normally run our war as an application using java -jar on the command line.
- There's a special folder; org/springframework/boot/loader/ which contains classes to launch boot executable application from war file. Please check out the last tutorial to understand why these special classes are needed. In the case of war file, probably WarLauncher will be used when running as executable war. Let's confirm that in the META-INF/Manifest.MF file:
Manifest-Version: 1.0
Implementation-Title: boot-war
Implementation-Version: 1.0-SNAPSHOT
Archiver-Version: Plexus Archiver
Built-By: Joe
Implementation-Vendor-Id: com.logicbig.example
Spring-Boot-Version: 1.4.3.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.WarLauncher
Start-Class: com.logicbig.example.WarStructureExample
Spring-Boot-Classes: WEB-INF/classes/
Spring-Boot-Lib: WEB-INF/lib/
Created-By: Apache Maven 3.0.5
Build-Jdk: 1.8.0_111
Implementation-URL: http://projects.spring.io/spring-boot/boot-war/
- There's no BOOT-INF as it was in the executable jar case (last tutorial)
- As all extra stuff consists of launcher classes and embedded server jars in lib-provided folder only, the boot war enhancement is only for executability of the war and it doesn't play any part when deployed to a servlet container.
Deploying war to tomcat server
Please follow the steps as stated in traditional war deployment section above.
Running the executable war
D:\examples\boot-war>java -jar target\boot-war-1.0-SNAPSHOT.war
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.4.3.RELEASE)
2017-01-16 13:17:26.616 INFO 5332 --- [ main] c.logicbig.example.WarStructureExample : Starting WarStructureExample v1.0-SNAPSHOT on JoeMsi with PID 5332 (D:\LogicBig\example-projects\spring-boot\boot-war\target\boot-war-1.0-SNAPSHOT.war started by Joe in D:\LogicBig\example-projects\spring-boot\boot-war)
2017-01-16 13:17:26.620 INFO 5332 --- [ main] c.logicbig.example.WarStructureExample : No active profile set, falling back to default profiles: default
2017-01-16 13:17:26.689 INFO 5332 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5a2e4553: startup date [Mon Jan 16 13:17:26 CST 2017]; root of context hierarchy
2017-01-16 13:17:28.155 INFO 5332 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2017-01-16 13:17:28.167 INFO 5332 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2017-01-16 13:17:28.169 INFO 5332 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.6
2017-01-16 13:17:29.102 INFO 5332 --- [ost-startStop-1] org.apache.jasper.servlet.TldScanner : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
2017-01-16 13:17:29.177 INFO 5332 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2017-01-16 13:17:29.177 INFO 5332 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2491 ms
2017-01-16 13:17:29.305 INFO 5332 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2017-01-16 13:17:29.308 INFO 5332 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-01-16 13:17:29.309 INFO 5332 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-01-16 13:17:29.309 INFO 5332 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-01-16 13:17:29.309 INFO 5332 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2017-01-16 13:17:29.522 INFO 5332 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5a2e4553: startup date [Mon Jan 16 13:17:26 CST 2017]; root of context hierarchy
2017-01-16 13:17:29.583 INFO 5332 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.logicbig.example.WarStructureExample$MyController.handler(org.springframework.ui.Model)
2017-01-16 13:17:29.586 INFO 5332 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-01-16 13:17:29.587 INFO 5332 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-01-16 13:17:29.615 INFO 5332 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-01-16 13:17:29.615 INFO 5332 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-01-16 13:17:29.659 INFO 5332 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-01-16 13:17:30.205 INFO 5332 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-01-16 13:17:30.274 INFO 5332 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-01-16 13:17:30.279 INFO 5332 --- [ main] c.logicbig.example.WarStructureExample : Started WarStructureExample in 4.13 seconds (JVM running for 4.502)
Output:
Traditional-war example projectDependencies and Technologies Used: - Spring Boot 1.4.3.RELEASE
Corresponding Spring Version 4.3.5.RELEASE - spring-boot-starter-web : Starter for building web, including RESTful, applications using Spring
MVC. Uses Tomcat as the default embedded container.
- spring-boot-starter-tomcat : Starter for using Tomcat as the embedded servlet container. Default
servlet container starter used by spring-boot-starter-web.
- JDK 1.8
- Maven 3.3.9
|