Lesson 5 fundamentals of spring boot 2 - interceptor, file upload, exception handling, native components and custom components
tags:
- Spring Boot
- 2021 Shang Silicon Valley
- Lei Fengyang
Section I interceptor
1.1 HandlerInterceptor interface
- At the bottom of the interceptor is the HandlerInterceptor interface, which has three methods
- The preHandle target method handles before processing
- The postHandle target method does not render the page after processing
- After completion page cleanup after rendering
- Use interceptors for landing checks
- Create interceptor class interceptor.LoginInterceptor to inherit HandlerInterceptor
- Create configuration class config.AdminWebConfig and override addInterceptors
- Specify interception rules * * if all static resources are intercepted, they will also be intercepted**
- Create interceptor class
package com.atguigu.interceptor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Interceptor for landing inspection * The HandlerInterceptor interface must be implemented * 1. Configure which requests the interceptor will intercept (configured in AdminWebConfig) * 2. Put these configurations in a container (override addInterceptors) * 3. Specify interception rules [if all static resources are intercepted, they will also be intercepted] */ @Slf4j public class LoginInterceptor implements HandlerInterceptor { /** * Before the target method is executed * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); log.info("The intercepted request path is{}", requestURI);; // Login check logic HttpSession session = request.getSession(); Object loginUser = session.getAttribute("loginUser"); if (loginUser != null){ // Release return true; } // Stop not logging in and jump to the login page request.setAttribute("msg", "Please log in first"); // response.sendRedirect("/"); // This can't get msg and forward it directly request.getRequestDispatcher("/").forward(request, response); return false; } /** * After the target method is executed * @param request * @param response * @param handler * @return * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("postHandle implement{}",modelAndView); } /** * After page rendering * @param request * @param response * @param handler * @return * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("afterCompletion Execution exception{}",ex); } }
- Create configuration class
package com.atguigu.config; import com.atguigu.interceptor.LoginInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class AdminWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**") // In this case, all requests will be intercepted, including static resources .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //Request for release } }
- If you do not use. excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js / * *"); Release static resources. It can also be configured in the configuration file.
# In this way, / static must be added before static resources/ spring.mvc.static-path-pattern=/static/**
1.2 interceptor principle
- In the breakpoint mainPage, find the handler that the HandlerExecutionChain can handle the request and all interceptors of the handler according to the current request.
- Execute the interceptor's applyPreHandle before the target method is executed. Follow in
if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }
- Traverse the interceptors one by one. It calls the preHandle method. Execute the preHandle methods of all interceptors sequentially first
- If the current interceptor prehandler returns true. Then execute the preHandle of the next interceptor
- If the current interceptor returns false. Execute afterCompletion of all executed interceptors directly in reverse order; It's just to clean up what has been executed before
- If any interceptor returns false. Jump out directly without executing the target method
- All interceptors return True. Execution target method
6. Execute the postHandle methods of all interceptors in reverse order.
7. Any exception in the previous steps will trigger afterCompletion in reverse order
8. After the page is successfully rendered, afterCompletion will also be triggered in reverse order
Section II file upload
2.1 realization of file upload function
- Write a FormTestController controller
package com.atguigu.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; /** * File upload test */ @Slf4j @Controller public class FormTestController { @GetMapping("/form_layouts") public String form_layout(){ return "form/form_layouts"; } /** * MultipartFile Automatically encapsulate uploaded files * @param email * @param username * @param headerImg * @param photos * @return */ @PostMapping("/upload") public String upload(@RequestParam("email") String email, @RequestParam("username") String username, @RequestPart("headerImg") MultipartFile headerImg, @RequestPart("photos") MultipartFile[] photos) throws IOException { log.info("Uploaded information: email={}, username={}, headerImg={},photos={}", email, username, headerImg.getSize(), photos.length); if (headerImg.isEmpty()){ // Save to file server or oss server String originalFilename = headerImg.getOriginalFilename(); headerImg.transferTo(new File("D:\\" + originalFilename)); } if (photos.length > 0){ for (MultipartFile photo : photos){ if (!photo.isEmpty()){ String originalFilename = photo.getOriginalFilename(); photo.transferTo(new File("D:\\" + originalFilename)); } } } return "main"; } }
- Configuration file sets the size of the uploaded file
# Size setting of each uploaded file spring.servlet.multipart.max-file-size=10MB # Upload size setting of all files in a request spring.servlet.multipart.max-request-size=100MB
2.2 file upload principle
- ctrl + N search MultipartAutoConfiguration
- The standard Servlet multipartresolver file upload parameter resolver is automatically configured (only files uploaded in Servlet mode can be parsed)
- Principle steps
- Or track processedRequest = checkMultipart(request) from DispatcherServlet#doDispatch; If it is Multipart, repackage the request.
- Parameter parser to parse the file content in the request and package it into MultipartFile
- Encapsulate the file information in the request into a Map; MultiValueMap<String, MultipartFile>
FileCopyUtils. Realize the copy of file stream
Section 3 exception handling
2.1 default rules
- Official website: https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-web-applications.spring-mvc.error-handling
- By default, Spring Boot provides / error mapping to handle all errors
- For the machine client, it generates a JSON response with details of the error, HTTP status, and exception messages.
- For the browser client, respond to a "whitelabel" error view and render the same data in HTML format
- Custom error page - put it in the error of templates
- error/404.html error/5xx.html; If there is an accurate error status code, the page will match accurately. If not, find 4xx.html; If none, trigger the white page
2.2 automatic configuration principle of exception handling
- org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration automatically configures exception handling rules
- Component 1 in container: Type: defaulterrorattributes - > ID: errorAttributes (default method name as id)
- public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver
- DefaultErrorAttributes: defines which data attributes can be included in the error page. The properties in the page that need to be customized need to be defined.
- Component 2 in the container: Type: basicErrorController -- > ID: basicErrorController (response json or white page adaptation response) Click to see it. The jump logic needs to be customized and defined.
- Handle the request for the default / error path; The page responds to new ModelAndView("error", model);
- There is a component in the container. View - > ID is error; (response to default error page) whiteableerrorwiewconfiguration
- Put the component BeanNameViewResolver (View resolver) in the container; Use the returned View name error as the id of the component to find the View object in the container.
- If you want to return to the page; You will find the error view [StaticView]. (the default is a white page)
- Component 3 in the container: Type: defaulterrorviewresolver - > ID: conventionErrorViewResolver Click to see it. The path of the error page needs to be defined.
- If an error occurs, the HTTP status code will be used as the view page address (viewName) to find the real page
- error/404,5xx.html
2.3 exception handling steps and processes
- Execute the target method ha.handle. Any exception during the operation of the target method will be caught and mark the end of the current request; And use dispatchException
- Enter the view parsing process (page rendering?)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); - mv = processHandlerException handle the exception occurred by handler, and return ModelAndView after processing;
- Traverse all handlerExceptionResolvers to see who can handle the current exception [HandlerExceptionResolver handler exception resolver]
- DefaultErrorAttributes is the default exception parser of the system;
- Track it in
- DefaultErrorAttributes handles exceptions first. Save the exception information to the rrequest field and return null; By default, no one can handle exceptions, so exceptions will be thrown
- 1. If no one can handle it, the bottom layer will send the / error request. It will be processed by the underlying BasicErrorController
- 2. Parse error view; Traverse all errorviewresolvers to see who can resolve them.
- 3. The default DefaultErrorViewResolver uses the response status code as the address of the error page, error/500.html
- 4. The template engine finally responds to this page error/500.html
2.4 custom error handling logic
- The first: custom error page
- error/404.html error/5xx.html; If there is an accurate error status code, the page will match accurately. If not, find 4xx.html; If none, trigger the white page
- 400 error code: no request parameter or wrong parameter type; Bad requests are generally browser parameters that are not passed correctly
- The second (recommended): @ ControllerAdvice+@ExceptionHandler handles global exceptions; The bottom layer is supported by the first ExceptionHandlerExceptionResolver in the second exception handler
package com.atguigu.exception; /* * Handle controller exceptions for the entire web */ import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @Slf4j @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler({ArithmeticException.class, NullPointerException.class}) // The exception handler handles exceptions public String handleArithException(Exception e){ log.error("The exception is: {}", e); return "login"; // View address } }
- The third type: * * @ responsestatus + custom exception * *; The bottom layer is the second ResponseStatusExceptionResolver in the second. The bottom layer calls response.sendError(statusCode, resolvedReason) with the information annotated by responsestatus; Finally, tomcat sends / error, and the request ends at the same time.
@GetMapping("/dynamic_table") public String dynamic_table(Model model){ //Traversal of table contents //response.sendError List<User> users = Arrays.asList(new User("zhangsan", "123456"), new User("lisi", "123444"), new User("haha", "aaaaa"), new User("hehe ", "aaddd")); model.addAttribute("users",users); if(users.size()>3){ throw new UserTooManyException(); } return "table/dynamic_table"; }
package com.atguigu.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value= HttpStatus.FORBIDDEN,reason = "Too many users") public class UserTooManyException extends RuntimeException { public UserTooManyException(){ } public UserTooManyException(String message){ super(message); } }
- The fourth: Spring underlying exceptions, such as parameter type conversion exceptions; The third DefaultHandlerExceptionResolver of the second handles the exceptions at the bottom of the framework.
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
@GetMapping("/basic_table") public String basic_table(@RequestParam("a") int a){ int i = 10/0; return "table/basic_table"; }
- The fifth is to customize the implementation of HandlerExceptionResolver to handle exceptions; It can be used as the default global exception handling rule. Priority needs to be defined to prevent it from being handled in advance by the default exception handler.
package com.atguigu.exception; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Order(value= Ordered.HIGHEST_PRECEDENCE) //Priority. The smaller the number, the higher the priority @Component public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { try { response.sendError(511,"I like mistakes"); } catch (IOException e) { e.printStackTrace(); } return new ModelAndView(); } }
- ErrorViewResolver implements custom exception handling;
- response.sendError . The error request will be forwarded to the controller
- No one can handle your exception. tomcat bottom response.sendError. The error request will be forwarded to the controller
- The page address that basicErrorController wants to go to is resolved by ErrorViewResolver; Guaranteed exception handling. All unhandled exceptions will be caught by it.
Section 4 native component injection (Servlet, Filter, Listener)
4.1 inject using Servlet API
- Step 1: @ ServletComponentScan(basePackages = "com.atguigu.admin") add in the startup class: specify where the native Servlet components are placed. This method is recommended;
// com.atguigu.Boot05WebAdminApplication startup class package com.atguigu; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @ServletComponentScan(basePackages = "com.atguigu") @SpringBootApplication public class Boot05WebAdminApplication { public static void main(String[] args) { SpringApplication.run(Boot05WebAdminApplication.class, args); } }
- Step 2: implement the injection of the following native components.
- @WebServlet(urlPatterns = "/my"): effect: direct response without Spring interceptor
- @WebFilter(urlPatterns={"/css/*","/images/*"})
- @WebListener
// WebServlet package com.atguigu.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(urlPatterns = "/my") public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("666666"); } }
// @WebFilter package com.atguigu.servlet; import lombok.extern.slf4j.Slf4j; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebServlet; import java.io.IOException; @Slf4j @WebFilter(urlPatterns = {"/css/*", "/images/*"}) public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("MyFilter Initialization complete;"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { log.info("MyFilter work"); chain.doFilter(request, response); } @Override public void destroy() { log.info("MyFilter Destroy;"); } }
// WebListener package com.atguigu.servlet; import lombok.extern.slf4j.Slf4j; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @Slf4j @WebListener public class MySwervletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { log.info("MySwervletContextListener Listening to the completion of project initialization"); } @Override public void contextDestroyed(ServletContextEvent sce) { log.info("MySwervletContextListener Item destruction detected"); } }
- Extension: how to register DispatchServlet
- The DispatcherServlet property is automatically configured in the container and bound to WebMvcProperties; The corresponding configuration file configuration item is spring.mvc.
- Configure DispatcherServlet through servletregistrationbean < DispatcherServlet >.
- The default mapping is / path.
# You can modify the default path of the servlet spring.mvc.servlet.path=/mvc/
- Tomcat-Servlet; Multiple servlets can handle the same layer path, and the principle of accurate optimization
- A: /my/
- B: /my/1
4.2 injection using RegistrationBean
- ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean
package com.atguigu.servlet; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Arrays; /** * 1,MyServlet --> /my * 2,DispatcherServlet --> / */ // (proxyBeanMethods = true): ensure that the dependent components are always single instance @Configuration(proxyBeanMethods = true) public class MyRegistConfig { @Bean public ServletRegistrationBean myServlet(){ MyServlet myServlet = new MyServlet(); return new ServletRegistrationBean(myServlet,"/my","/my02"); } @Bean public FilterRegistrationBean myFilter(){ MyFilter myFilter = new MyFilter(); // return new FilterRegistrationBean(myFilter,myServlet()); FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter); filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*")); return filterRegistrationBean; } @Bean public ServletListenerRegistrationBean myListener(){ MySwervletContextListener mySwervletContextListener = new MySwervletContextListener(); return new ServletListenerRegistrationBean(mySwervletContextListener); } }
Section V embedded Servlet container
5.1 switching embedded Servlet container
- Webserver supported by default (official website manual 7.4.3)
- Tomcat, Jetty, or Undertow
- The servletwebserver ApplicationContext container starts looking for the ServletWebServerFactory and guides the creation of the server
- Switch servers (if you do not want to use the default Tomcat server, modify the following configuration to exclude exclusion and add other server configurations)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
- principle
- SpringBoot application startup found that it is currently a web application. Web scenario package - Import tomcat
- The web application will create a web version of the ioc container servletwebserver ApplicationContext
- When the Servlet webserver ApplicationContext starts, look for the Servlet webserverfactory (Servlet's web server factory - > Servlet's web server)
- There are many WebServer factories at the bottom of SpringBoot by default; TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory
- There will be an automatic configuration class directly at the bottom. ServletWebServerFactoryAutoConfiguration ServletWebServerFactoryAutoConfiguration imported servletwebserverfactoryautoconfiguration (configuration class)
- The ServletWebServerFactoryConfiguration configuration class dynamically determines which Web Server package is imported into the system. (the default is to import the tomcat package from the web starter), and there is TomcatServletWebServerFactory in the container
- Tomcat servlet webserverfactory creates a Tomcat server and starts it; The constructor of Tomcat webserver has the initialization method initialize - this.tomcat.start();
- The embedded server is to manually call the code that starts the server (the tomcat core jar package exists)
5.2 custom Servlet container
- Implement webserverfactorycustomizer < configurableservletwebserverfactory >
- Bind the value of the configuration file to the servlet webserverfactory
- First: modify the configuration file server.xxx, for example:
server.tomcat.accesslog.max-days=10
- The second is to directly customize the configurableservlet webserverfactory
- The third type: xxxxxxcustomizer: customizer, which can change the default rules of xxxx
Section VI customization principle
6.1 common ways of customization
- Modify the configuration file;
- xxxxxCustomizer;** Customizer**
- Write a custom configuration class xxxConfiguration; +@ Bean replaces and adds default components in the container; view resolver
- Write a configuration class for a web application to implement WebMvcConfigurer to customize web functions; +@ Bean extends some more components to the container
@Configuration public class AdminWebConfig implements WebMvcConfigurer
- @EnableWebMvc + WebMvcConfigurer - @ Bean can fully take over spring MVC, and all rules can be reconfigured by itself, such as static resources, view parser, default page, etc; Realize customization and extension functions
- principle
- Webmvcoautoconfiguration is the default spring MVC auto configuration feature class. Static resources, welcome pages, etc
- Once you use @ EnableWebMvc. Meeting @ Import(DelegatingWebMvcConfiguration.class)
- DelegatingWebMvcConfiguration only guarantees the most basic use of spring MVC
- Get the WebMvcConfigurer from all systems. The customization of all functions takes effect when these webmvcconfigurers are combined
- Some very low-level components are automatically configured. RequestMappingHandlerMapping and the dependent components of these components are obtained from the container
- public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
- The configuration in webmvcconfiguration must be @ ConditionalOnMissingBean(WebMvcConfigurationSupport.class) to be effective
- @EnableWebMvc caused webmvcaiutoconfiguration not to take effect. Basically, the default configuration will not take effect.
6.2 customized principle analysis routine
- Scenario starter - xxxautoconfiguration - Import xxx components - bind xxxProperties - bind profile items