Sprboot Error Handling Mechanism and Embedded Servlet Container

It mainly includes error handling mechanism, custom error processing page and json data, and embedded servlet container.

1. Error handling mechanism

1. SpringBoot default error handling mechanism

Default effect:

1. Browser, return a default error page

The browser sends the request header:

2. If it's another client, it responds to a json data by default

Principle:

Error Mvc AutoConfiguration; automatic configuration of error handling;

Add the following components to the container

​ 1,DefaultErrorAttributes:

Help us share information on the page;
@Override
	public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
			boolean includeStackTrace) {
		Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
		errorAttributes.put("timestamp", new Date());
		addStatus(errorAttributes, requestAttributes);
		addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
		addPath(errorAttributes, requestAttributes);
		return errorAttributes;
	}

2. Basic Error Controller: Processing default/error requests

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
    
    @RequestMapping(produces = "text/html")//Generate html-type data; requests sent by browsers come to this method for processing
	public ModelAndView errorHtml(HttpServletRequest request,
			HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
				request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
		response.setStatus(status.value());
        
        //Which page to go to as the error page; contains the page address and page content
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
		return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
	}

	@RequestMapping
	@ResponseBody    //Generate json data, other clients come to this method to process;
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		Map<String, Object> body = getErrorAttributes(request,
				isIncludeStackTrace(request, MediaType.ALL));
		HttpStatus status = getStatus(request);
		return new ResponseEntity<Map<String, Object>>(body, status);
	}

​ 3,ErrorPageCustomizer:

	@Value("${error.path:/error}")
	private String path = "/error"; when the system has errors, it comes to error request for processing; (error page rule for web.xml registration)

​ 4,DefaultErrorViewResolver:

@Override
	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
			Map<String, Object> model) {
		ModelAndView modelAndView = resolve(String.valueOf(status), 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) {
        //By default SpringBook can find a page error/404
		String errorViewName = "error/" + viewName;
        
        //Template engine can parse this page address using template engine.
		TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
				.getProvider(errorViewName, this.applicationContext);
		if (provider != null) {
            //Return to the view address specified by errorViewName when the template engine is available
			return new ModelAndView(errorViewName, model);
		}
        //The template engine is not available. Find the page error/404.html corresponding to errorViewName under the static resource folder
		return resolveResource(errorViewName, model);
	}

Steps:

Once a system has errors like 4xx or 5xx, ErrorPage Customizer will take effect (customizing the response rules for errors); an error request will come; it will be processed by Basic ErrorController;

1. Response page; which page to go is parsed by DefaultErrorViewResolver;

protected ModelAndView resolveErrorView(HttpServletRequest request,
      HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
    //All ErrorViewResolver s get ModelAndView
   for (ErrorViewResolver resolver : this.errorViewResolvers) {
      ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
      if (modelAndView != null) {
         return modelAndView;
      }
   }
   return null;
}

2. Customize error response:

1. How to customize the wrong page;

1. In the case of template engine, error/status code; [Name the error page as error status code. Put html under the error folder in the template engine folder], the error of this status code will come to the corresponding page.

We can use 4xx and 5xx as the file names of the error pages to match all errors of this type, with exact priority (first looking for exact status codes. html);

Information available on the page;

Timestamp: timestamp

status: status code

error: error prompt

exception: exception object

Message: Exceptional message

Erors: All the errors in JSR303 data validation are here

2. There is no template engine (the template engine can't find this error page), search under the static resource folder;

3. There are no error pages above, that is, the default error prompt page of SpringBoot.

2. How to customize the wrong json data;

Custom exception handling-returning custom json data;

@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());
        return map;
    }
}
//There is no adaptive effect.

2. Forwarding to/error for adaptive response effect processing

 @ExceptionHandler(UserNotExistException.class)
    public String handleException(Exception e, HttpServletRequest request){
        Map<String,Object> map = new HashMap<>();
        //Pass in our own error status code 4xx 5xx, otherwise we won't enter the custom error page parsing process
        /**
         * Integer statusCode = (Integer) request
         .getAttribute("javax.servlet.error.status_code");
         */
        request.setAttribute("javax.servlet.error.status_code",500);
        map.put("code","user.notexist");
        map.put("message",e.getMessage());
        //Forwarding to / error
        return "forward:/error";
    }

3. Carry out our customized data;

When an error occurs, it comes to the / error request, which is processed by Basic Error Controller, and the data available in response is obtained by getError Attributes (the method specified by AbstractError Controller).

1. Completely write an implementation class of ErrorController (or a subclass of AbstractErrorController) and put it in a container.

2. The data that can be used on the page, or the data that can be returned by json, are obtained through errorAttributes.getErrorAttributes.

DefaultErrorAttributes.getErrorAttributes(); Data processing by default;

Customize ErrorAttributes

//Add our own Error Attributes to the container
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
        map.put("company","atguigu");
        return map;
    }
}

Ultimately, the response is adaptive, and you can customize ErrorAttributes to change what you want to return.

2. Configuring Embedded Servlet Containers

SpringBoot defaults to Tomcat as an embedded Servlet container.

1. How to customize and modify the configuration of Servlet container;

1. Modify the server-related configuration (Server Properties [also Embedded Servlet Container Customizer]);

server.port=8081
server.context-path=/crud

server.tomcat.uri-encoding=UTF-8

//General Servlet container settings
server.xxx
//Tomcat settings
server.tomcat.xxx

2. Write an Embedded Servlet Container Customizer: a customizer for embedded Servlet containers; to modify the configuration of Servlet containers

@Bean  //Be sure to add this customizer to the container
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
    return new EmbeddedServletContainerCustomizer() {

        //Rules for customizing embedded Servlet containers
        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            container.setPort(8083);
        }
    };
}

2. Register three components of Servlet [Servlet, Filter, Listener]

Because SpringBoot defaults to launching an embedded Servlet container in the form of a jar package to start SpringBoot's web application, there is no web.xml file.

Register the three components in the following way

ServletRegistrationBean

//Register Three Components
@Bean
public ServletRegistrationBean myServlet(){
    ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
    return registrationBean;
}

FilterRegistrationBean

@Bean
public FilterRegistrationBean myFilter(){
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new MyFilter());
    registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
    return registrationBean;
}

ServletListenerRegistrationBean

@Bean
public ServletListenerRegistrationBean myListener(){
    ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
    return registrationBean;
}

SpringBook automatically registers the front-end controller of Spring MVC when it helps us automate Spring MVC; DIspatcher Servlet;

In Dispatcher Servlet AutoConfiguration:

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration(
      DispatcherServlet dispatcherServlet) {
   ServletRegistrationBean registration = new ServletRegistrationBean(
         dispatcherServlet, this.serverProperties.getServletMapping());
    //Default interception: / all requests; package static resources, but do not intercept jsp requests; / * intercepts jsp
    //The default intercepted request path of Spring MVC front-end controller can be modified by server.servletPath
    
   registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
   registration.setLoadOnStartup(
         this.webMvcProperties.getServlet().getLoadOnStartup());
   if (this.multipartConfig != null) {
      registration.setMultipartConfig(this.multipartConfig);
   }
   return registration;
}

2. Can SpringBook support other Servlet containers?

3. Replace with other embedded Servlet containers

Default support:

Tomcat (default)

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   The default of introducing web module is to use embedded Tomcat as Servlet container.
</dependency>

Jetty (for long connections, such as point-to-point chat)

<!-- Introduce web Modular -->
<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>

<!--Introducing others Servlet container-->
<dependency>
   <artifactId>spring-boot-starter-jetty</artifactId>
   <groupId>org.springframework.boot</groupId>
</dependency>

Undertow (does not support jsp, but is a high-performance non-blocking servlet container with good concurrency performance)

<!-- Introduce web Modular -->
<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>

<!--Introducing others Servlet container-->
<dependency>
   <artifactId>spring-boot-starter-undertow</artifactId>
   <groupId>org.springframework.boot</groupId>
</dependency>

4. The principle of automatic configuration of embedded Servlet container;

Embedded Servlet Container AutoConfiguration: Automatic Configuration of Embedded Servlet Containers

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
//Import BeanPost Processors Registrar: Spring annotation; import some components into the container
//Import Embedded Servlet Container Customizer Bean PostProcessor:
//Post Processor: Before and after bean initialization (object creation, not assignment) to perform initialization
public class EmbeddedServletContainerAutoConfiguration {
    
    @Configuration
	@ConditionalOnClass({ Servlet.class, Tomcat.class })//Determine whether Tomcat dependencies are currently introduced;
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)//Judging that the current container does not have user-defined Embedded Servlet Container Factory: Embedded Servlet Container Factory; Role: Create Embedded Servlet Container
	public static class EmbeddedTomcat {

		@Bean
		public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
			return new TomcatEmbeddedServletContainerFactory();
		}

	}
    
    /**
	 * Nested configuration if Jetty is being used.
	 */
	@Configuration
	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
			WebAppContext.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedJetty {

		@Bean
		public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
			return new JettyEmbeddedServletContainerFactory();
		}

	}

	/**
	 * Nested configuration if Undertow is being used.
	 */
	@Configuration
	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedUndertow {

		@Bean
		public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
			return new UndertowEmbeddedServletContainerFactory();
		}

	}

1. Embedded Servlet Container Factory (Embedded Servlet Container Factory)

public interface EmbeddedServletContainerFactory {

   //Getting Embedded Servlet Containers
   EmbeddedServletContainer getEmbeddedServletContainer(
         ServletContextInitializer... initializers);

}

2. Embedded Servlet Container: (Embedded Servlet Container)

3. Take Tomcat Embedded Servlet Container Factory as an example

@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
      ServletContextInitializer... initializers) {
    //Create a Tomcat
   Tomcat tomcat = new Tomcat();
    
    //Basic links for configuring Tomcat
   File baseDir = (this.baseDirectory != null ? this.baseDirectory
         : createTempDir("tomcat"));
   tomcat.setBaseDir(baseDir.getAbsolutePath());
   Connector connector = new Connector(this.protocol);
   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 in the configured Tomcat, return an Embedded Servlet Container, and start the Tomcat server
   return getTomcatEmbeddedServletContainer(tomcat);
}

4. The Effective Principle of the Configuration Modification of Embedded Containers

ServerProperties,EmbeddedServletContainerCustomizer

Embedded Servlet Container Customizer: Customizer helps us modify the configuration of Servlet containers

5. Embedded Servlet Container Customizer Bean PostProcessor is imported into the container

//Before initialization
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    //If the current initialization is a component of Configurable Embedded ServletContainer type
   if (bean instanceof ConfigurableEmbeddedServletContainer) {
       //
      postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
   }
   return bean;
}

private void postProcessBeforeInitialization(
			ConfigurableEmbeddedServletContainer bean) {
    //Get all the customizers and call the customize method of each customizer to assign attributes to the Servlet container.
    for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
        customizer.customize(bean);
    }
}

private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
    if (this.customizers == null) {
        // Look up does not include the parent context
        this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
            this.beanFactory
            //Get all components of this type from the container: Embedded Servlet Container Customizer
            //Customize the Servlet container to add an Embedded Servlet Container Customizer-type component to the container
            .getBeansOfType(EmbeddedServletContainerCustomizer.class,
                            false, false)
            .values());
        Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
        this.customizers = Collections.unmodifiableList(this.customizers);
    }
    return this.customizers;
}

ServerProperties It's also a customizer

Steps:

1. SpringBook adds the corresponding Embedded Servlet Container Factory to the container according to the import dependency.

2. An object created by a component in the container will startle the post processor; Embedded Servlet Container Customizer Bean PostProcessor;

As long as it is an embedded Servlet container factory, the post processor will work.

3. Postprocessor, get all Embedded Servlet Container Customizer from container, call customization method of customizer

5. Embedded Servlet container startup principle;

When will an embedded Servlet container factory be created? When to get the embedded Servlet container and start Tomcat?

Get the embedded Servlet container factory:

1. SpringBoot Application Start run Method

2. refreshContext(context);SpringBoot refreshes IOC container (create IOC container object, initialize container, create each component in container); If a web application creates Annotation Config Embedded Web Application Context, otherwise: Annotation Config Application Context

3. refresh(context); refresh the ioc container just created;

public void refresh() 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.
         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();
      }
   }
}

4. onRefresh (); the ioc container of the web rewrites the onRefresh method

5. webioc container will create embedded Servlet container; create Embedded Servlet Container ();

6. Get the Embedded Servlet Container Factory:

EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();

Get the Embedded Servlet ContainerFactory component from the ioc container; Tomcat Embedded Servlet ContainerFactory creates an object, and as soon as the post processor sees this object, it gets all the customizers to customize the configuration of the Servlet container first.

7. Use container factory to obtain embedded Servlet container: this. embedded Servlet Container = containerFactory. getEmbedded Servlet Container (getSelfInitializer ());

8. The embedded Servlet container creates objects and starts the Servlet container.

Firstly, the embedded Servlet container is started, and then the remaining objects in the ioc container are retrieved.

IOC Container Start to Create Embedded Servlet Container

Tags: Tomcat Spring SpringBoot JSON

Posted on Tue, 10 Sep 2019 20:23:27 -0700 by callmecheez