(6) Error handling mechanism

1. SpringBoot’s default error handling mechanism

1) The browser returns to the default error screen with some display information

The header from which the browser sends the request

2) Using Postman to request access, will return json array

Postman Specifies the request header that sends the request

How the default processing mechanism works:

ErrorMvcAutoConfiguration configured in the Mvc the wrong way, is the information of error handling in the Mvc SpringBoot automatic configuration, through to the IoC container to add components to complete the configuration.

Automatic configuration adds the following components to the container:

① TerrorAttributes: Error message handling by default

② BasicErrorController: Handles default /error requests

③ ErrorPageCustomizer: Indicates the error page

Terrorviewresolver: Set a default error view

Specific functions of these components:

① TerrorAttributes: Used for error message handling

@Order(Ordered.HIGHEST_PRECEDENCE)
public class DefaultErrorAttributes implements ErrorAttributes.HandlerExceptionResolver.Ordered {...@Override
        @Deprecated
        public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> errorAttributes = new LinkedHashMap<>();
        // Store the timestamp to
        errorAttributes.put("timestamp".new Date());
        addStatus(errorAttributes, webRequest);
        addErrorDetails(errorAttributes, webRequest, includeStackTrace);
        addPath(errorAttributes, webRequest);
        return errorAttributes;
    }

    // Retrieve the error status code, error message, etc
    private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
        Integer status = getAttribute(requestAttributes, RequestDispatcher.ERROR_STATUS_CODE);
        if (status == null) {
            errorAttributes.put("status".999);
            errorAttributes.put("error"."None");
            return;
        }
        errorAttributes.put("status", status);
        try {
            errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
        }
        catch (Exception ex) {
            // Unable to obtain a reason
            errorAttributes.put("error"."Http Status "+ status); }}// Displays detailed error information, including exceptions, obtaining exception paths, and obtaining exception information
    private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest,
                                 boolean includeStackTrace) {
        Throwable error = getError(webRequest);
        if(error ! =null) {
            while (error instanceofServletException && error.getCause() ! =null) {
                error = error.getCause();
            }
            errorAttributes.put("exception", error.getClass().getName());
            if(includeStackTrace) { addStackTrace(errorAttributes, error); } } addErrorMessage(errorAttributes, webRequest, error); }... }Copy the code

② BasicErrorController: Handles default /error requests

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {...// Generate htML-type data; Requests sent by the browser come to this method for processing
    @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
	public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map<String, Object> model = Collections
				.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
		response.setStatus(status.value());
        // Specify the error page and include the address and content of the error page
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
		return(modelAndView ! =null)? modelAndView :new ModelAndView("error", model);
	}

    // Generates JSON data, and requests from other clients come to this method for processing
	@RequestMapping
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		HttpStatus status = getStatus(request);
		if (status == HttpStatus.NO_CONTENT) {
			return new ResponseEntity<>(status);
		}
		Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
		return newResponseEntity<>(body, status); }... }Copy the code

The ModelAndView method calls the resolveErrorView method for the page response.

protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map
       
         model)
       ,> {
    // Iterate through all ErrorViewResolver to get ModelAndView
    for (ErrorViewResolver resolver : this.errorViewResolvers) {
        ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
        if(modelAndView ! =null) {
            returnmodelAndView; }}return null;
}
Copy the code

The specific page pointing is resolved by the DefaulViewResolver class, which is explained below.

③ ErrorPageCustomizer: Indicates that the configuration error page is redirected

private final DispatcherServletPath dispatcherServletPath;

protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {
    this.properties = properties;
    this.dispatcherServletPath = dispatcherServletPath;
}
Copy the code

The ErrorPageCustomizer class assigns a value to dispatcherServletPath in the class associated with registering error messages

@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
    ErrorPage errorPage = new ErrorPage(
        this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
    errorPageRegistry.addErrorPages(errorPage);
}
Copy the code

The resulting request path is /error

@Value("${error.path:/error}")
private String path = "/error";// The system sends an error request after an error occurs, which is equivalent to the error rule registered in web.xml
Copy the code

Terrorviewresolver: Configure default error view display

Return in ErrorMvcAutoConfiguration DefaultErrorViewResolver

@Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean(ErrorViewResolver.class)
DefaultErrorViewResolver conventionErrorViewResolver(a) {
    return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
}
Copy the code

Page information that defines the response in the DefaulViewResolver class

public class DefaultErrorViewResolver implements ErrorViewResolver.Ordered {

    private static final Map<Series, String> SERIES_VIEWS;

    // Define the response code
    static {
        Map<Series, String> views = new EnumMap<>(Series.class);
        views.put(Series.CLIENT_ERROR, "4xx");
        views.put(Series.SERVER_ERROR, "5xx"); SERIES_VIEWS = Collections.unmodifiableMap(views); }...@Override
        public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
        }
        return modelAndView;
    }

    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        // SpringBoot finds a page by default? Error/pages named with status codes (e.g. Error /404)
        String errorViewName = "error/" + viewName;
        // If the template engine can parse the page address, use the template engine to parse it
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
                                                                                               this.applicationContext);
        if(provider ! =null) {
            // Return the view address specified by errorViewName if the template engine is available
            return new ModelAndView(errorViewName, model);
        }
        // If the template engine is not available, look for the page corresponding to errorViewName in the static resources folder. Such as: the error / 404 HTML
        returnresolveResource(errorViewName, model); }}Copy the code

An error message after ErrorMvcAutoConfiguration processing steps:

  • When a 4XX or 5XX error occurs in the system;
  • ErrorPageCustomizerThe effect (bean used to customize the wrong response rule) is set to send/errorRequests;
  • /errorThe request will beBasicErrorControllerProcess and respond to HTML pages or JSON data;
  • Response to HTML page or JSON data byDefaultErrorViewResolverReturn after configuration.

2. Customize the error response

2.1 How can I Customize the Error Page

1) With a template engine

Error/status code: The error page is named as the error status code. HTML is stored in the error folder in the template engine folder. When an error occurs, the corresponding page is displayed.

We can also use 4xx and 5xx as the file name of the error page to match all errors of this type, precisely locate the first (that is, find the page corresponding to the status code first, and use this page only when it cannot be found).

Information available on the page:

The retrieved page information is placed in the Shared Page Information Defaulattributes class

  • Timestamp: indicates the timestamp
  • Status: indicates the status code
  • Error: indicates an error message
  • Exception: exception object
  • Message: JSR303 data validation errors are here

2) Without a template engine

If the error page is not found in the template engine, look for it in the static resources folder

3) Error pages with none of the above

The default SpringBoot error page is used

2.2 How can I Customize Incorrect Data

1) Custom exception handling & return custom JSON data

// There is no adaptive effect...
@ControllerAdvice
public class MyExceptionHandler {
    @ResponseBody
    @ExceptionHandler(UserNotExistException.class)
    public Map<String,Object> handleException(Exception e){
        Map<String,Object> map = new HashMap<>();
        map.put("code"."user.notexist");
        map.put("message",e.getMessage());
        returnmap; }}Copy the code

2) Forward to /error for adaptive response effect processing

@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(UserNotExistException.class)
    public String handleException(Exception e, HttpServletRequest request) {
        Map<String, Object> map = new HashMap<>();
        // Pass the custom error status code 4xx 5xx
        request.setAttribute("javax.servlet.error.status_code".500);
        map.put("code"."user.notexist");
        map.put("message"."User error");

        request.setAttribute("ext", map);
        // Forward to the /error page, where the browser and other clients respond differently, handled by the BasicErrorController
        return "forward:/error"; }}Copy the code

2.3 Pass on customized data

Spring Boot handling procedures when exceptions occur:

  • When a page exception occurs, a /error request is sent;
  • The sent /error request will be sentBasicErrorControllerClass to handle;
  • Response out can obtain the data bygetErrorAttributesAccess to (ErrorControllerThe implementation of the classAbstractErrorControllerIn)

So the way we pass data:

Write an implementation class for ErrorController (or subclass AbstractErrorController) and inject it into the container.

The data that is available on the page, or the data that is returned from json is retrieved using terrorAttributes’ getErrorAttributes method. Terrorattributes in the container does data processing by default. Therefore, we can customize ErrorAttributes to achieve error message transmission.

Code examples:

// Inject our custom ErrorAttributes into the container
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {

    // The map of the return value is the field that the page and JSON can get
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
        Map<String, Object> map = super.getErrorAttributes(webRequest, options);
        map.put("author"."Bruce");

        // We exception the data carried by the handler
        Map<String, Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", RequestAttributes.SCOPE_REQUEST);
        map.put("ext", ext);
        returnmap; }}Copy the code

End result: The response is adaptive, and you can customize the ErrorAttributes to change what needs to be returned

(7) Servlet container related

SpringBoot uses an embedded servlet container by default (with Tomcat embedded as the servlet container)

1. Embedded Servlet container

1.1 Modify and customize the configuration of the embedded Servlet container

(1) Modify the server configuration through the configuration file (specified in the ServerProperties class)

# server. XXX is a server-specific configuration
server.port=9083
server.servlet.context-path=/servlet
# server.tomcat. XXX is a Tomcat related configuration
server.tomcat.uri-encoding=utf-8
Copy the code

(2) by implementing WebServerFactoryCustomizer interface, modified WebServerFactoryCustomizer is embedded servlet container custom, can be used to modify the servlet container configuration

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
    @Override
    public void customize(ConfigurableWebServerFactory factory) {
        factory.setPort(9000); }}Copy the code

(3) through ConfigurableServletWebServerFactory subclasses of custom configuration

@Configuration
public class MyServerConfig {
    /** * You can also create examples of Tomcat customizers for more configuration *@return* /
    @Bean
    public ConfigurableServletWebServerFactory webServerFactory(a) {

        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.setPort(9090);
        returnfactory; }}Copy the code

It is found that the Spring Boot configuration execution priority @Component > application.properties > @bean is not clear whether it is accurate or not. Further research on the principle of automatic configuration is needed to determine.

Configuration of Embedded Servlet containers in SpringBoot: Embedded Servlet Container Support

1.2 Register three Servlet components [Servlet, Filter, Listener]

By default, SpringBoot starts the embedded Servlet container as a JAR package to start SpringBoot’s Web reference, since there is no web.xml

File, so the registration three components are configured using @beans.

① Register the Servlet component and use the ServletRegistrationBean

A custom Servlet

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("this is my servlet"); }}Copy the code

A configuration Bean that writes a ServletRegistrationBean type in the configuration class registers the Servlet component

@Bean
public ServletRegistrationBean myServlet(a) {

    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new MyServlet(), "/myServlet");
    servletRegistrationBean.setLoadOnStartup(1);
    return servletRegistrationBean;
}
Copy the code

② Register the Filter component

A custom filter

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("MyFilter is running ......");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy(a) {}}Copy the code

The configuration Bean that writes type FilterRegistrationBean in the configuration class registers the Filter component

@Bean
public FilterRegistrationBean myFilter(a) {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    filterRegistrationBean.setFilter(new MyFilter());
    filterRegistrationBean.setUrlPatterns(Arrays.asList("/test"."/myServlet"));
    return filterRegistrationBean;
}
Copy the code

③ Register the Listener component

Custom Listener

public class MyListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ContextInitialized: The Web application is started...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ContextDestroyed Web application destruction..."); }}Copy the code

Writing ServletListenerRegistrationBean configuration types in the configuration class registered Bean Listener component

@Bean
public ServletListenerRegistrationBean myListener(a) {
    ServletListenerRegistrationBean<MyListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean<>(new MyListener());
    return servletListenerRegistrationBean;
}
Copy the code

SpringBoot will automatically register SpringMVC front-end controller DispatcherServlet when SpringMVC is automatically configured. Through DispatcherServletAutoConfiguration class loading DispatcherServlet

@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {

    @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
    @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider
       
         multipartConfig)
        {
        // Intercepts by default: /, that is, all requests, including static resources, but not JSP requests. /* Intercepts JSPS
        // You can also change the default SpringMVC front-end controller intercept request path by setting spring.vcv.servlet. path in the configuration file
        DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
                                                                                               webMvcProperties.getServlet().getPath());
        registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
        registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
        multipartConfig.ifAvailable(registration::setMultipartConfig);
        returnregistration; }}Copy the code

1.3 Using other embedded Servlet containers

By default, SpringBoot uses Tomcat as the built-in Servlet container. We can also switch to other Servlet containers such as Jetty (suitable for long connections), Undertow (does not support JSP).

Servlet container diagram supported by Spring Boot by default

Built-in Servlet containers supported by default in SpringBoot:

① Tomcat container (default)

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

② Use Jetty as a Servlet container

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>

<! Use Jetty as a Servlet container -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Copy the code

③ Use Undertow as the Servlet container

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>

<! -- Using undertow as Servlet container -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
Copy the code

Tomcat Jetty Undertow Comparison of three Servlet containers: Undertow,Tomcat and Jetty server configuration details and performance tests

1.4 Principles of automatic configuration of embedded Servlet containers

EmbeddedWebServerFactoryCustomizerAutoConfiguration SpringBoot embedded servlet container automatically configured in the class, has displayed in the automatic configuration SpringBoot support servlet container: Tomcat, Jetty, Undertow, Netty, Tomcat is used by default. Determine which Servlet container to use based on the Starter that the POM file imports.

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {

	/** * Nested configuration if Tomcat is being used. */
	@Configuration(proxyBeanMethods = false)
    // Check whether Tomcat dependencies are imported
	@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
	public static class TomcatWebServerFactoryCustomizerConfiguration {

		@Bean
		public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
			return newTomcatWebServerFactoryCustomizer(environment, serverProperties); }}/** * Nested configuration if Jetty is being used. */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class })
	public static class JettyWebServerFactoryCustomizerConfiguration {

		@Bean
		public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
			return newJettyWebServerFactoryCustomizer(environment, serverProperties); }}/** * Nested configuration if Undertow is being used. */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })
	public static class UndertowWebServerFactoryCustomizerConfiguration {

		@Bean
		public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
			return newUndertowWebServerFactoryCustomizer(environment, serverProperties); }}/** * Nested configuration if Netty is being used. */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(HttpServer.class)
	public static class NettyWebServerFactoryCustomizerConfiguration {

		@Bean
		public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
			return newNettyWebServerFactoryCustomizer(environment, serverProperties); }}}Copy the code

Servlet container auto-configuration principle:

1) ServletWebServerFactory is a Web server customizer (Servlet customization Factory) : Create Servlet containers and create corresponding Servlet containers according to different categories

@FunctionalInterface
public interface ServletWebServerFactory {

	/**
	 * Gets a new fully configured but paused {@link WebServer} instance. Clients should
	 * not be able to connect to the returned server until {@link WebServer#start()} is
	 * called (which happens when the {@code ApplicationContext} has been fully
	 * refreshed).
	 * @param initializers {@link ServletContextInitializer}s that should be applied as
	 * the server starts
	 * @return a fully configured and started {@link WebServer}
	 * @see WebServer#stop()
	 */
	WebServer getWebServer(ServletContextInitializer... initializers);

}
Copy the code

Inheritance diagram

2) WebServer is the parent of the Servlet container class

public interface WebServer {

	/**
	 * Starts the web server. Calling this method on an already started server has no
	 * effect.
	 * @throws WebServerException if the server cannot be started
	 */
	void start(a) throws WebServerException;

	/**
	 * Stops the web server. Calling this method on an already stopped server has no
	 * effect.
	 * @throws WebServerException if the server cannot be stopped
	 */
	void stop(a) throws WebServerException;

	/**
	 * Return the port this server is listening on.
	 * @return the port (or -1 if none)
	 */
	int getPort(a);

	/**
	 * Initiates a graceful shutdown of the web server. Handling of new requests is
	 * prevented and the given {@code callback} is invoked at the end of the attempt. The
	 * attempt can be explicitly ended by invoking {@link #stop}. The default
	 * implementation invokes the callback immediately with
	 * {@link GracefulShutdownResult#IMMEDIATE}, i.e. no attempt is made at a graceful
	 * shutdown.
	 * @param callback the callback to invoke when the graceful shutdown completes
	 * @since2.3.0 * /
	default void shutDownGracefully(GracefulShutdownCallback callback) { callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE); }}Copy the code

Inheritance diagram

In order toTomcatServletWebServerFactoryThe automatic configuration of the Servlet container is described as follows:

1) create a Tomcat container factory TomcatServletWebServerFactory

public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
    implements ConfigurableTomcatWebServerFactory.ResourceLoaderAware {...@Override
        public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        // Create a Tomcat
        Tomcat tomcat = new Tomcat();
        // Configure basic Tomcat content
        File baseDir = (this.baseDirectory ! =null)?this.baseDirectory : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        configureEngine(tomcat.getEngine());
        for (Connector additionalConnector : this.additionalTomcatConnectors) {
            tomcat.getService().addConnector(additionalConnector);
        }
        prepareContext(tomcat.getHost(), initializers);
        // Pass the configured Tomcat into the getTomcatWebServer method, return the Tomcat container, and start the container
        returngetTomcatWebServer(tomcat); }...// Pass configured Tomcat to TomcatWebServer, create Tomcat container and return
        protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
        return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown()); }... }Copy the code

② The constructor of TomcatWebServer is used to initialize Tomcat container

public class TomcatWebServer implements WebServer {.../**
	 * Create a new {@link TomcatWebServer} instance.
	 * @param tomcat the underlying Tomcat server
	 * @param autoStart if the server should be started
	 * @param shutdown type of shutdown supported by the server
	 * @since2.3.0 * /
        // Initialize the Tomcat container through the constructor
        public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
        initialize();
    }

    // Initialize the Tomcat container
    private void initialize(a) throws WebServerException {
        logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
        synchronized (this.monitor) {
            try {
                addInstanceIdToEngineName();

                Context context = findContext();
                context.addLifecycleListener((event) -> {
                    if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
                        // Remove service connectors so that protocol binding doesn't
                        // happen when the service is started.removeServiceConnectors(); }});// Start the server to trigger initialization listeners
                this.tomcat.start();

                // We can re-throw failure exception directly in the main thread
                rethrowDeferredStartupExceptions();

                try {
                    ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
                }
                catch (NamingException ex) {
                    // Naming is not enabled. Continue
                }

                // Unlike Jetty, all Tomcat threads are daemon threads. We create a
                // blocking non-daemon to stop immediate shutdown
                startDaemonAwaitThread();
            }
            catch (Exception ex) {
                stopSilently();
                destroySilently();
                throw new WebServerException("Unable to start embedded Tomcat", ex); }}}... }Copy the code

3) Modify the configuration of embedded Servlet containers

① Modify the configuration file, that is, configure the configuration information in ServerProperties. Configure the Servlet container by reading the configuration information

(2) by implementing WebServerFactoryCustomizer interface, customize the Servlet container

How to modify configuration principles for the Servlet container

4) Spring ServletWebServerFactoryAutoConfiguration class for automatic configuration in the Boot

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
// Import the backend handler to import the configuration related to the embedded Servlet container and the configuration classes related to the Servlet container
public class ServletWebServerFactoryAutoConfiguration {

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }

    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

    @Bean
    @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
    @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
    public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter(a) {
        ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
        FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
        registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registration;
    }

    /**
	 * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
	 * {@link ImportBeanDefinitionRegistrar} for early registration.
	 */
    public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar.BeanFactoryAware {

        private ConfigurableListableBeanFactory beanFactory;

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            if (beanFactory instanceof ConfigurableListableBeanFactory) {
                this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; }}/ / registered registerBeanDefinitions, rear call WebServerFactoryCustomizerBeanPostProcessor processor
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            if (this.beanFactory == null) {
                return;
            }
            registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
                                           WebServerFactoryCustomizerBeanPostProcessor.class);
            registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
                                           ErrorPageRegistrarBeanPostProcessor.class);
        }

        private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class
        beanClass) {
            if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true.false))) {
                RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
                beanDefinition.setSynthetic(true); registry.registerBeanDefinition(name, beanDefinition); }}}}Copy the code

In WebServerFactoryCustomizerBeanPostProcessor web service factory custom components post processor, responsible for the bean to perform initialization work before initialization. Obtained from the IoC container type for WebServerFactoryCustomizer types of components.

Post-handler: before and after bean initialization (after object creation, no assignment, initialization)

public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor.BeanFactoryAware {

    private ListableBeanFactory beanFactory;

    privateList<WebServerFactoryCustomizer<? >> customizers;@Override
    public void setBeanFactory(BeanFactory beanFactory) {
        Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
                            "WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");
        this.beanFactory = (ListableBeanFactory) beanFactory;
    }

    // Before initializing the Bean
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // If a component of type WebServerFactory is currently initialized
        if (bean instanceof WebServerFactory) {
            postProcessBeforeInitialization((WebServerFactory) bean);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    // Get all the customizers and call each one's customize method to assign attributes to the Servlet container;
    @SuppressWarnings("unchecked")
    private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
            .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
            .invoke((customizer) -> customizer.customize(webServerFactory));
    }

    // 
    privateCollection<WebServerFactoryCustomizer<? >> getCustomizers() {if (this.customizers == null) {
            // Look up does not include the parent context
            this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
            this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
            this.customizers = Collections.unmodifiableList(this.customizers);
        }
        return this.customizers;
    }

    // Get the components of the custom Servlet container
    @SuppressWarnings({ "unchecked", "rawtypes" })
    privateCollection<WebServerFactoryCustomizer<? >> getWebServerFactoryCustomizerBeans() {return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false.false).values(); }}Copy the code

For example, is obtained by post processor for TomcatServletWebServerFactoryCustomizer calls to customize () custom method, obtain the Servlet container related configuration class serverProperties for automatic configuration

public class TomcatServletWebServerFactoryCustomizer
		implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered {

	private final ServerProperties serverProperties;

	public TomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
		this.serverProperties = serverProperties;
	}

	@Override
	public int getOrder(a) {
		return 0;
	}

    // Customize the Servlet container with this method
	@Override
	public void customize(TomcatServletWebServerFactory factory) {
        // Get the configuration information in the configuration class serverProperties
		ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
		if(! ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) { factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns()); }if(tomcatProperties.getRedirectContextRoot() ! =null) { customizeRedirectContextRoot(factory, tomcatProperties.getRedirectContextRoot()); } customizeUseRelativeRedirects(factory, tomcatProperties.isUseRelativeRedirects()); factory.setDisableMBeanRegistry(! tomcatProperties.getMbeanregistry().isEnabled()); }private void customizeRedirectContextRoot(ConfigurableTomcatWebServerFactory factory, boolean redirectContextRoot) {
		factory.addContextCustomizers((context) -> context.setMapperContextRootRedirectEnabled(redirectContextRoot));
	}

	private void customizeUseRelativeRedirects(ConfigurableTomcatWebServerFactory factory,
			boolean useRelativeRedirects) { factory.addContextCustomizers((context) -> context.setUseRelativeRedirects(useRelativeRedirects)); }}Copy the code

Summary of embedded Servlet container automatic configuration principles:

1) SpringBoot, according to the dependence on import situation automatically add the corresponding to the container WebServerFactoryCustomizer components (web service customization)

A component to create objects (2) when the container will trigger WebServerFactoryCustomizerBeanPostProcessor post processor

(3) WebServerFactoryCustomizerBeanPostProcessor custom components (Web factory rear processor) to get all the services the factory custom components (i.e. WebServerFactoryCustomizer interface, Custom custom components) call the Customize custom interface and customize the Servlet container configuration;

④ The embedded Servlet container factory creates the Tomcat container, initializes and starts the Servlet container

1.5 Startup principle of the embedded Servlet Container

When the embedded Servlet container factory is created and when to get the embedded Servlet container and start Tomcat

Get the embedded Servlet container factory:

1) Start the Spring Boot application through the main method entrance and run the run method;

2) Call the run method of the Application class, and call the refreshContext method to refresh the container. If it is a web application is created AnnotationConfigServletWebServerApplicationContext container, or create the default IoC container AnnotationConfigApplicationContext container

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        // Create IoC containers. Create different IoC containers for different environments
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                         new Class[] { ConfigurableApplicationContext.class }, context);
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // Refresh the container, that is, create container objects and initialize them to create individual component objects in the container
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}
Copy the code

There is much more to the IoC container in Spring, which will be covered in more detail in a later blog post.

3) call the refresh method to refresh the container, after a series of calls to AbstractApplicationContext class the refresh method.

@Override
public void refresh(a) throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            Refresh the IoC container here
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...resetCommonCaches(); }}}Copy the code

4) AbstractApplicationContext onRefresh method in the class of refresh the IoC container, including web IoC container rewrite the onRefresh method, called polymorphic calls through the parent class.

5) call ServletWebServerApplication createWebServer method in the class, let the web IoC create embedded Servlet container

private void createWebServer(a) {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        // Get the embedded Servlet factory
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
        getBeanFactory().registerSingleton("webServerGracefulShutdown".new WebServerGracefulShutdownLifecycle(this.webServer));
        getBeanFactory().registerSingleton("webServerStartStop".new WebServerStartStopLifecycle(this.this.webServer));
    }
    else if(servletContext ! =null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}
Copy the code

6) call ServletWebServerApplication class getWebServerFactory create factory method to create the Servlet

protected ServletWebServerFactory getWebServerFactory(a) {
    // Use bean names so that we don't consider the hierarchy
    // Get the ServletWebServerFactory component from the IoC container via the getBean method
    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    if (beanNames.length == 0) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
                                              + "ServletWebServerFactory bean.");
    }
    if (beanNames.length > 1) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
                                              + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
Copy the code

GetWebServerFactory method through getBeanFactory (.) getBeanNamesForType (ServletWebServerFactory. Class); Get the ServletWebServerFactory component from the IoC container. We can see through the debug debugging for tomcatServletWebServerFactory components in the IoC container. We’re SpringBoot automatic configuration class: ServletWebServerFactoryAutoConfiguration class, injected tomcatServletWebServerFactory components

After we get tomcatServletWebServerFactory components, Enter the tomcatServletWebServerFactory class to create the Servlet container object will trigger the post processor WebServerFactoryCustomizerBeanPostProcessor at this time, The post-processor gets all the customizers to customize the configuration of the Servlet container class.

7) through the container factory for embedded Servlet container, namely through tomcatServletWebServerFactory create tomcatWebServer class;

8) Create an embedded Servlet container and start the container. The Tomcat WebServer class contains the configuration and startup methods for the Tomcat container.

The embedded Servlet container is first started and then the other components in the IoC container are created, that is, the Servlet container is created when the IoC container is started.

The IoC container is not covered in detail in this chapter and will be covered in a future blog post.

2. External Servlet containers

  • Embedded Servlet container: The application is packaged as an executable JAR package, and the application is launched by executing the JAR package.
    • Advantages: Easy to start, easy to switch between different embedded Servlet containers, and no external Servlet container installation
    • Disadvantages: No JSP support, more complex optimization customization, configuration through the configuration file or custom Servlet to create factoriesWebServerFactoryCustomizer
  • External Servlet container: Package the application into a WAR package. Use the external Servlet container, for example, run the war package using Tomcat installed and configured

2.1 Usage of the external Servlet container

① Set the package mode of the project to WAR package.

② Set the project not to use the built-in Servlet container;

(3) you must write SpringBootServletInitializer implementation class, and implement the configure method;

④ Start the WAR package through the external Servlet container.

Specific operation:

① Set the package mode of the project to WAR package. (Note the directory structure used to create webApp)

Specify, or modify the project’s POM.xml file when using the SpringBoot project creation tool

<packaging>war</packaging>
Copy the code

② Set the project not to use the built-in Servlet container;

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

(3) you must write SpringBootServletInitializer implementation class, and implement the configure method;

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        // Call the configure method, passing in the SpringBoot application main program
        returnapplication.sources(Springboot04JspApplication.class); }}Copy the code

④ Start the WAR package through the external Servlet container.

For details on how to configure the Tomcat server in IDEA, see the blog post: Change the SpringBoot project to external Tomcat startup

2.2 Startup principle of the external Servlet container

Execution steps for different packaging methods:

  • Jar package: Execute main method of SpringBoot main class, create and start IoC container, create embedded Servlet container;
  • War file: start the server, application server startup SpringBoot SpringBootServletInitializer 】 【, create and start the IoC container;

The Servlet 3.1Shared libraries / runtimes pluggabilitySpecification:

  • Each jar in the current Web application is created when the server starts (when the Web application starts)ServletContainerInitializerInstance;
  • ServletContainerInitializerThe implementation class must be placed in the JAR packageMETA - INF/servicesThe folder contains a file namedjavax.servlet.ServletContainerInitializerFile, the content isServletContainerInitializerThe full class name of the implementation class of
  • You can also use@HandlesTypesAnnotations to load the classes we’re interested in when we start the application;

Process:

① Start the Tomcat server.

(2) org \ springframework \ spring – web \ 5.2.10 RELEASE \ spring – web – 5.2.10. The jar! \ meta-inf \ services \ javax.mail servlet. ServletContainerInitializer Spring web module contains the file, the contents of the file: Org. Springframework. Web. SpringServletContainerInitializer, i.e., application startup need to create this class;

(3) SpringServletContainerInitializer will @ HandlesTypes (WebApplicationInitializer. Class) all WebApplicationInitializer annotations Types of classes are passed to the onStartup method, for these WebApplicationInitializer type of class instance is created;

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

    @Override
    public void onStartup(@NullableSet<Class<? >> webAppInitializerClasses, ServletContext servletContext)
        throws ServletException {

        List<WebApplicationInitializer> initializers = new LinkedList<>();

        if(webAppInitializerClasses ! =null) {
            for(Class<? > waiClass : webAppInitializerClasses) {// Be defensive: Some servlet containers provide us with invalid classes,
                // no matter what @HandlesTypes says...
                if(! waiClass.isInterface() && ! Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {try {
                        / / for these WebApplicationInitializer type of class instance is created
                        initializers.add((WebApplicationInitializer)
                                         ReflectionUtils.accessibleConstructor(waiClass).newInstance());
                    }
                    catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); }}}}if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }

        servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
        AnnotationAwareOrderComparator.sort(initializers);
        for (WebApplicationInitializer initializer : initializers) {
            / / every WebApplicationInitializer type of class call their ` onStartup ` method;initializer.onStartup(servletContext); }}}Copy the code

(4) each WebApplicationInitializer type of class call their onStartup method;

WebApplicationInitializerAn inheritance diagram of an interface

(5) is equivalent to our SpringBootServletInitializer implementation class object is created, and perform the onStartup method;

6 SpringBootServletInitializer instance execution onStartup method called when createRootApplicationContext method to create the IoC container;

All landowners createRootApplicationContext method invokes the run method start SpringBoot application and create the IoC container

SpringBootServletInitializerSource:

public abstract class SpringBootServletInitializer implements WebApplicationInitializer {...// Execute the onStartup method
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
        // Logger initialization is deferred in case an ordered
        // LogServletContextInitializer is being used
        this.logger = LogFactory.getLog(getClass());
        // Create an IoC container
        WebApplicationContext rootApplicationContext = createRootApplicationContext(servletContext);
        if(rootApplicationContext ! =null) {
            servletContext.addListener(new SpringBootContextLoaderListener(rootApplicationContext, servletContext));
        }
        else {
            this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not "
                              + "return an application context"); }}...// The method to create an IoC container
        protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
        / / create SpringApplicationBuilder
        SpringApplicationBuilder builder = createSpringApplicationBuilder();
        builder.main(getClass());
        ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
        if(parent ! =null) {
            this.logger.info("Root context already created (using as parent).");
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
            builder.initializers(new ParentContextApplicationContextInitializer(parent));
        }
        builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
        builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
        // Call the configure method, which subclasses override to pass in the main SpringBoot program
        builder = configure(builder);
        builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
        Call the build method to create a Spring application
        SpringApplication application = builder.build();
        if(application.getAllSources().isEmpty() && MergedAnnotations.from(getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) { application.addPrimarySources(Collections.singleton(getClass())); } Assert.state(! application.getAllSources().isEmpty(),"No SpringApplication sources have been defined. Either override the "
                     + "configure method or add an @Configuration annotation");
        // Ensure error pages are registered
        if (this.registerErrorPageFilter) {
            application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
        }
        application.setRegisterShutdownHook(false);
        // Call the run method to start the SpringBoot application
        returnrun(application); }...// configure method, subclass implementation after the master boot class
        protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        returnbuilder; }... }Copy the code

Therefore, the process of starting the SpringBoot application through the external Servlet container is as follows: Start the external Servlet container first, and then start the SpringBoot application.

There will be a more detailed article about the IoC container in Spring. ヾ(≧▽≦*)o