Start using built-in tomcat
Configuration case
Start mode
-
Main function startup in IDEA
-
mvn springboot-run
-
java -jar
When using this method, nohup will be used to ensure that the service runs in the background.nohup java -jar -Xms128m -Xmx128m -Xss256k -XX:+PrintGCDetails -XX:+PrintHeapAtGC -Xloggc:/data/log/ >/data/log/ &
Using java -jar will not start any embedded Application Server by default. This command just starts a JVM process that executes jar main. When spring-boot-starter-web contains the embedded tomcat server dependency, execute java -jar Application Server will be started
Configure built-in tomcat properties
All the properties about Tomcat are inIt is defined in the configuration class, and we only need to configure the configuration properties. Common Servlet container configurations are based on
server
as prefix
#Configure the program port, the default is 8080
=8080
#User session session expiration time, in seconds
=
#Configure the default access path, the default is /
-path=
The Tomcat-specific configurations are all based onas prefix
# Configure Tomcat encoding, the default is UTF-8
-encoding=UTF-8
#Configure the maximum number of threads
-threads=1000
Note: Using the built-in tomcat does not require tomcat-embed-jasper and spring-boot-starter-tomcat dependencies, because tomcat has been integrated in the spring-boot-starter-web dependency.
principle
Starting from the main function
public static ConfigurableApplicationContext run(Class
exceptionReporters = new ArrayList();
();
SpringApplicationRunListeners listeners = (args);
();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = (listeners, applicationArguments);
(environment);
//Print the banner. You can doodle here and replace it with the logo of your own project.
Banner printedBanner = (environment);
//Create application context
context = ();
exceptionReporters = (, new Class[]{}, context);
//Preprocessing context
(context, environment, listeners, applicationArguments, printedBanner);
//Refresh context
(context);
//Refresh the context again
(context, applicationArguments);
(context);
(context, applicationArguments);
} catch (Throwable var10) {
}
try {
(context);
return context;
} catch (Throwable var9) {
}
}
Since we want to know how tomcat is started in Spring Boot, in the run method, we focus on creating the application context (createApplicationContext) and refreshing the context (refreshContext).
Create context
//Create context
protected ConfigurableApplicationContext createApplicationContext() {
Class
The AnnotationConfigServletWebServerApplicationContext class will be created here. The AnnotationConfigServletWebServerApplicationContext class inherits ServletWebServerApplicationContext, and this class finally integrates AbstractApplicationContext.
refresh context
//
//Refresh context
private void refreshContext(ConfigurableApplicationContext context) {
(context);
if () {
try {
();
} catch (AccessControlException var3) {
}
}
}
//Here directly call the final parent class() method
protected void refresh(ApplicationContext applicationContext) {
((AbstractApplicationContext)applicationContext).refresh();
}
//
public void refresh() throws BeansException, IllegalStateException {
synchronized() {
();
ConfigurableListableBeanFactory beanFactory = ();
(beanFactory);
try {
(beanFactory);
(beanFactory);
(beanFactory);
();
();
//Call the onRefresh() method of each subclass, which means we have to go back to the subclass: ServletWebServerApplicationContext and call the onRefresh() method of that class.
();
();
(beanFactory);
();
} catch (BeansException var9) {
();
(var9);
throw var9;
} finally {
();
}
}
}
//
//When I see a familiar face in this method, the mysterious veil is about to be unveiled.
protected void onRefresh() {
();
try {
();
} catch (Throwable var2) {
}
}
//
//Here is creating webServer, but tomcat has not been started yet. Here it is created through ServletWebServerFactory, then look at ServletWebServerFactory
private void createWebServer() {
WebServer webServer = ;
ServletContext servletContext = ();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = ();
= (new ServletContextInitializer[]{()});
} else if (servletContext != null) {
try {
().onStartup(servletContext);
} catch (ServletException var4) {
}
}
();
}
//interface
public interface ServletWebServerFactory {
WebServer getWebServer(ServletContextInitializer... initializers);
}
//accomplish
AbstractServletWebServerFactory
JettyServletWebServerFactory
TomcatServletWebServerFactory
UndertowServletWebServerFactory
Here, the ServletWebServerFactory interface has four implementation classes, corresponding to four types of containers:
Among them, there are two commonly used ones: TomcatServletWebServerFactory and JettyServletWebServerFactory.
//
//Here we use tomcat, so we check TomcatServletWebServerFactory. Here I finally saw traces of tomcat.
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = ( != null) ? : createTempDir("tomcat");
(());
//Create Connector object
Connector connector = new Connector();
().addConnector(connector);
customizeConnector(connector);
(connector);
().setAutoDeploy(false);
configureEngine(());
for (Connector additionalConnector : ) {
().addConnector(additionalConnector);
}
prepareContext((), initializers);
return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}
//
//Return to the Engine container. Seeing this, if you are familiar with the tomcat source code, you will not be unfamiliar with the engine.
public Engine getEngine() {
Service service = getServer().findServices()[0];
if (() != null) {
return ();
}
Engine engine = new StandardEngine();
( "Tomcat" );
(hostname);
(createDefaultRealm());
(engine);
return engine;
}
//Engine is the highest level container, Host is a sub-container of Engine, Context is a sub-container of Host, and Wrapper is a sub-container of Context.
The getWebServer method creates a Tomcat object and does two important things: adding the Connector object to tomcat, configureEngine(());
The getWebServer method returns TomcatWebServer.
//
//Call the constructor here to instantiate TomcatWebServer
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
(tomcat, "Tomcat Server must not be null");
= tomcat;
= autoStart;
initialize();
}
private void initialize() throws WebServerException {
//You will see this log in the console
("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized () {
try {
addInstanceIdToEngineName();
Context context = findContext();
((event) -> {
if ((()) && Lifecycle.START_EVENT.equals(())) {
removeServiceConnectors();
}
});
//===Start tomcat service===
();
rethrowDeferredStartupExceptions();
try {
(context, (), getClass().getClassLoader());
}
catch (NamingException ex) {
}
//Enable blocking non-daemon process
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
//
public void start() throws LifecycleException {
getServer();
();
}
//Here we will return to TomcatWebServer
public void stop() throws LifecycleException {
getServer();
();
}
//
//Start the tomcat service
@Override
public void start() throws WebServerException {
synchronized () {
if () {
return;
}
try {
addPreviouslyRemovedConnectors();
Connector connector = ();
if (connector != null && ) {
performDeferredLoadOnStartup();
}
checkThatConnectorsHaveStarted();
= true;
//Print this log in the console. If the context is set in yml, it will be printed here.
("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
+ getContextPath() + "'");
}
catch (ConnectorStartFailedException ex) {
stopSilently();
throw ex;
}
catch (Exception ex) {
throw new WebServerException("Unable to start embedded Tomcat server", ex);
}
finally {
Context context = findContext();
(context, (), getClass().getClassLoader());
}
}
}
//Close tomcat service
@Override
public void stop() throws WebServerException {
synchronized () {
boolean wasStarted = ;
try {
= false;
try {
stopTomcat();
();
}
catch (LifecycleException ex) {
}
}
catch (Exception ex) {
throw new WebServerException("Unable to stop embedded Tomcat", ex);
}
finally {
if (wasStarted) {
();
}
}
}
}
Deploy using external tomcat
Configuration case
Click here to start SpringBoot source code with external Tomcat
Inherit SpringBootServletInitializer
- If you deploy an external container, you cannot rely on the main function of Application. Instead, you need to start the Spring application context in a way similar to file configuration. At this time, you need to inherit SpringBootServletInitializer in the startup class and rewrite the configure method; also add @ SpringBootApplication annotation, this is to scan all Spring annotated beans
Method 1: The startup class inherits SpringBootServletInitializer to implement configure:
@SpringBootApplication
public class SpringBootHelloWorldTomcatApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return ();
}
}
The function of this class is similar to that of configuring the listener responsible for initializing the Spring application context, except that there is no need to write additional XML files here.
Method 2: Add a new class to inherit SpringBootServletInitializer to implement configure:
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
//Here is the startup class with @SpringBootApplication annotation
return ();
}
}
Modify tomcat related configurations
First you need to turn jar into war<packaging>war</packaging>
If you want toThe final packaging form is changed to warIf so, you still need to modify the file. Because spring-boot-starter-web contains an embedded tomcat container, direct deployment in an external container will cause conflicts and errors. Therefore, it is necessary to exclude the built-in tomcat
<dependency>
<groupId></groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId></groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
Here you need to remove the dependence on embedded Tomcat, so that the generated war package will not contain Tomcat-related jar packages in the lib directory, otherwise a startup error will occur.
However, after removing tomcat, the original sevlet was also removed, so additional service packages need to be introduced.
<dependency>
<groupId></groupId>
<artifactId>-api</artifactId>
<version>3.0.1</version>
</dependency>
Things to note
The name of the package created at this time should be consistent with -path=/test
<build>
<finalName>test</finalName>
</build>
If the context is different when published to tomcat webapps, the context will change.
principle
Tomcat will not take the initiative to start the springboot application, so it must be called when tomcat startsSpringBootServletInitializerSpringApplicationBuilder will start springboot.
The implementation of ServletContainerInitializer is placed in the META-INF/services folder of the jar package. There is a file named. The content is the full class name of the implementation class of ServletContainerInitializer. When the servlet container starts, it will find the implementation class of ServletContainerInitializer in this file, thereby creating an instance of it and calling onstartUp. It’s used hereSPI mechanism
HandlesTypes()
- The classes passed in by @HandlesTypes are of interest to ServletContainerInitializer
- The container will automatically find the WebApplicationInitializer in the classpath and pass it into the webAppInitializerClasses of the onStartup method.
-
Set<Class<?>> webAppInitializerClasses
This also includes the previously defined TomcatStartSpringBoot
@HandlesTypes()
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<>>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class> waiClass : webAppInitializerClasses) {
// If it is not an interface, it is not abstract, and it is related to WebApplicationInitializer, it will be instantiated.
if (!() && !(()) &&
(waiClass)) {
try {
((WebApplicationInitializer)
(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (()) {
("No Spring WebApplicationInitializer types detected on classpath");
return;
}
(() + "Spring WebApplicationInitializers detected on classpath");
// sort
(initializers);
for (WebApplicationInitializer initializer : initializers) {
(servletContext);
}
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Logger initialization is deferred in case an ordered
// LogServletContextInitializer is being used
= (getClass());
WebApplicationContext rootApplicationContext = createRootApplicationContext(servletContext);
if (rootApplicationContext != null) {
(new SpringBootContextLoaderListener(rootApplicationContext, servletContext));
}
else {
("No ContextLoaderListener registered, as createRootApplicationContext() did not "
+ "return an application context");
}
}
SpringBootServletInitializer
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
("Root context already created (using as parent).");
(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
(new ParentContextApplicationContextInitializer(parent));
}
(new ServletContextApplicationContextInitializer(servletContext));
();
// call configure
builder = configure(builder); //①
(new WebEnvironmentPropertySourceInitializer(servletContext));
SpringApplication application = ();//②
if (().isEmpty()
&& (getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent()) {
((getClass()));
}
(!().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if () {
(());
}
(false);
return run(application);//③
}
① When you call configure, you will come to TomcatStartSpringBoot.configure, and pass the Springboot startup class into
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return ();
}
② Call SpringApplication application = (); and a SpringApplication will be built based on the incoming Springboot startup class.
public SpringApplication build(String... args) {
configureAsChildIfNecessary(args);
();
return ;
}
③ Call return run(application); to start the springboot application
protected WebApplicationContext run(SpringApplication application) {
return (WebApplicationContext) ();
}
This is equivalent to starting the Main function:
public static void main(String[] args) {
(, args);
}
The subsequent process is consistent with the Main function using the built-in Tomcat above.