SpringMVC study notes three

1. Interceptor

1.1 Application scenarios

       ~~~~~~       If we want to perform some processing before or after the execution of multiple Handler methods, even in some cases, we need to intercept them and prevent the Handler methods from executing. Then we can use the interceptor provided by SpringMVC.

1.2 The difference between interceptors and filters

       ~~~~~~      Filters are processed before or after Servlet execution. The interceptor is to process the Handler (processor) before and after execution. As shown in the picture:
insert image description here

1.3 Create and configure interceptors

①Create a class to implement the HandlerInterceptor interface

@Component
public class FirstInterceptorimplements HandlerInterceptor {
    
    
}

② Implementation method

@Component
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        System.out.println("FirstInterceptor--->preHandle");
        //返回值代表是否放行,如果为true则放行,如果为fasle则拦截,目标方法执行不到
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
        System.out.println("FirstInterceptor--->postHandle");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        System.out.println("FirstInterceptor--->afterCompletion");
    }

③ Configure the interceptor

<!--配置拦截器-->
    <mvc:interceptors>
        <!-- bean和ref标签所配置的拦截器默认对DispatcherServlet处理的所有的请求进行拦截-->
        <!--<bean class="com.lx.interceptor.FirstInterceptor"></bean>-->
        <!--<ref bean="firstInterceptor"></ref>-->
        <mvc:interceptor>
            <!--配置需要拦截的请求的请求路径, /**表示所有请求-->
            <mvc:mapping path="/**"/>
            <!--配置需要排除拦截的请求的请求路径-->
            <mvc:exclude-mapping path="/abc"/>
            <!--配置使用哪个拦截器-->
            <ref bean="firstInterceptor"/>
        </mvc:interceptor>
        <ref bean="secondInterceptor"/>
    </mvc:interceptors>

1.4 Detailed explanation of interceptor methods and parameters

  • The preHandle method will be executed before the Handler method is executed, and we can perform some pre-judgment or processing in it.
  • The postHandle method will be executed after the Handler method is executed, where we can modify the data in the domain, or modify the page to be redirected.
  • The afterCompletion method will be executed at the end. At this time, there is no way to modify the data in the domain, and there is no way to modify the page to be redirected. We generally release some resources in this method.

preHandle() is executed in the order of configuration, while postHandle() and afterCompletion() are executed in reverse order of configuration

1.5 Execution order of multiple interceptors

If we configure multiple interceptors, the order of the interceptors is in the order of configuration.
The execution sequence of the methods in these interceptors is shown in the figure (when preHandler all returns true):
insert image description here
If the return value of the preHandle method of interceptor 3 is false. The execution sequence is as shown in the figure:
insert image description here

  • The postHandle method will only be executed if all interceptors are released.

  • Only when the current interceptor is released, the afterCompletion method of the current interceptor will be executed.

2. Unified exception handling

       ~~~~~~       In our actual project, the exceptions of the Dao layer and the Service layer will be thrown to the Controller layer. But if we add abnormal try...catch processing in the Controller method, it will be very cumbersome.
       ~~~~~~      So SpringMVC provides us with a unified exception handling solution. The exceptions in the Controller layer can be handled uniformly. This not only improves the reusability of the code but also decouples the exception handling code from our business code.
       ~~~~~~      One is to implement the HandlerExceptionResolver interface, and the other is to use the @ControllerAdvice annotation.

2.1 HandlerExceptionResolver

① Implement the interface

public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    
    

}

② Override method

public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    
    
    
    //如果handler中出现了异常,就会调用到该方法,我们可以在本方法中进行统一的异常处理。
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    
    
        //获取异常信息,把异常信息放入域对象中
        String msg = ex.getMessage();
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg",msg);
        //跳转到error.jsp
        modelAndView.setViewName("/WEB-INF/page/error.jsp");
        return modelAndView;
    }
}

③Inject into the container
       ~~~~~~      You can use annotation injection or xml configuration injection. Annotation injection is used here. Add the @Component annotation to the class, and be careful to ensure that the class can be scanned by the component.

@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    
    
	//....省略无关代码
}

2.2 @ControllerAdvice (important)

①Create a class and add @ControllerAdvice annotation for identification

@ControllerAdvice
public class MyControllerAdvice {
    
    

}

② Define the exception handling method
Define the exception handling method, and use @ExceptionHandler to identify the exception that can be handled.

//将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
    
    
    @ExceptionHandler(ArithmeticException.class)
    public String handleExecption(Throwable ex, Model model){
    
    
        model.addAttribute("ex",ex);
        return "error";
    }
}

③Inject into the container
       ~~~~~~      You can use annotation injection or xml configuration injection. Annotation injection is used here. Add the @ControllerAdvice annotation to the class, and be careful to ensure that the class can be scanned by the component.

2.3 Summary

       ~~~~~~       In actual projects, we generally choose to use @ControllerAdvice for unified handling of exceptions.
       ~~~~~~      Because if in a project where the front and back ends are not separated, the exception handling usually jumps to the error page, so that the user has a better experience. In projects where front and back ends are separated, exception handling generally involves encapsulating exception information into Json and writing it into the response body. In either case, using @ControllerAdvice can be implemented more conveniently.
       ~~~~~~      For example, the following method is an exception handling scheme that separates the front and back ends, encapsulates the exception information into an object, converts it into json, and writes it into the response body.

@ControllerAdvice
@Component
public class MyControllerAdvice {
    
    

    @ExceptionHandler({
    
    NullPointerException.class,ArithmeticException.class})
    @ResponseBody
    public Result handlerException(Exception ex){
    
    
        Result result = new Result();
        result.setMsg(ex.getMessage());
        result.setCode(500);
        return result;
    }
}

3. File upload

3.1 File upload requirements

       ~~~~~~      The Http protocol stipulates the request format requirements when we upload files. Therefore, when uploading files, in addition to adding a form item (input tag, type=file) for uploading files to the form, the following conditions must be met to upload.

①The request method is POST request
       ~~~~~~      If you are using a form to submit, you can set the method attribute of the form tag to POST. For example:

    <form action="/upload" method="post">

    </form>

②The Content-Type of the request header must be multipart/form-data
       ~~~~~~      If you are using a form, you can set the entype attribute of the form to multipart/form-data. For example:

    <form action="/upload" method="post" enctype="multipart/form-data">

    </form>

3.2 SpringMVC receives uploaded files

       ~~~~~~      SpringMVC uses the commons-fileupload package to encapsulate file uploads. We only need to introduce relevant dependencies and configure accordingly to easily implement the file upload function.
① dependence

		<!--commons文件上传,如果需要文件上传功能,需要添加本依赖-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>

②Configuration

 	 <!--
            文件上传解析器
            注意:id 必须为 multipartResolver
        -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设置默认字符编码 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 一次请求上传的文件的总大小的最大值,单位是字节-->
        <property name="maxUploadSize" value="#{1024*1024*100}"/>
        <!-- 每个上传文件大小的最大值,单位是字节-->
        <property name="maxUploadSizePerFile" value="#{1024*1024*50}"/>
    </bean>

③Receive the uploaded file data and process
the upload form

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="uploadFile">
        <input type="submit">
    </form>
</body>
</html>
@Controller
public class UploadController {
    
    

    @RequestMapping("/upload")
    public String upload(MultipartFile uploadFile) throws IOException {
    
    
        //文件存储 把上传上来的文件存储下来
        uploadFile.transferTo(new File("test.sql"));
        return "success";
    }
}

注意:方法参数名要和提交上来的参数名一致。

3.3 Common usage of MultipartFile

  • Get the original name of the uploaded file

    uploadFile.getOriginalFilename()

  • Get the MIME type of a file type

    uploadFile.getContentType()

  • Get the size of the uploaded file

    uploadFile.getSize()

  • Get the input stream corresponding to the uploaded file

    uploadFile.getInputStream()

4. File download

4.1 File Download Requirements

       ~~~~~~       If we want to provide the function of file download. The HTTP protocol requires us to meet the following rules.
①Set the response header Content-Type
       ~~~~~~      It is required to provide the MIME type of the downloaded file as the value of the response header Content-Type
② Set the response header Content-disposition
       ~~~~~~      It is required to write the URL-encoded value of the file name into the response header Content-disposition. However, it is required to comply with the following format, because this can solve the problem of garbled Chinese file names in different browsers.

Content-disposition: attachment; filename=%E4%B8%8B%E6%B5%B7%E5%81%9Aup%E4%B8%BB%E9%82%A3%E4%BA%9B%E5%B9%B4.txt;filename*=utf-8''%E4%B8%8B%E6%B5%B7%E5%81%9Aup%E4%B8%BB%E9%82%A3%E4%BA%9B%E5%B9%B4.txt

③Write the file data into the response body

4.2 Code implementation

       ~~~~~~      We can use the previously encapsulated download tool class to realize file download

Tool class code:

import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.net.URLEncoder;

public class DownLoadUtils {
    
    
    /**
     * 该方法可以快速实现设置两个下载需要的响应头和把文件数据写入响应体
     * @param filePath 该文件的相对路径
     * @param context  ServletContext对象
     * @param response
     * @throws Exception
     */
    public static void downloadFile(String filePath, ServletContext context, HttpServletResponse response) throws Exception {
    
    
        String realPath = context.getRealPath(filePath);
        File file = new File(realPath);
        String filename = file.getName();
        FileInputStream fis = new FileInputStream(realPath);
        String mimeType = context.getMimeType(filename);//获取文件的mime类型
        response.setHeader("content-type",mimeType);
        String fname= URLEncoder.encode(filename,"UTF-8");
        response.setHeader("Content-disposition","attachment; filename="+fname+";"+"filename*=utf-8''"+fname);
        ServletOutputStream sos = response.getOutputStream();
        byte[] buff = new byte[1024 * 8];
        int len = 0;
        while((len = fis.read(buff)) != -1){
    
    
            sos.write(buff,0,len);
        }
        sos.close();
        fis.close();
    }
}

Handler method definition

@Controller
public class DownLoadController {
    
    

    @RequestMapping("/download")
    public void download(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
        //文件下载
        DownLoadUtils.downloadFile("/WEB-INF/file/下海做UP主那些年.txt",request.getServletContext(),response);
    }
}

Another way to download is to use ResponseEntity<byte[]> as the return value

    	@RequestMapping("/test/down")
        public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException{
    
    
        //获取ServletContext对象
        ServletContext servletContext = session.getServletContext();
        //获取服务器中文件的真实路径
        String realpath = servletContext.getRealPath("/img/1.jpg");
        //创建输入流
        InputStream is = new FileInputStream(realpath);
        //创建字节数组
        byte[] bytes = new byte[is.available()];
        //将流读到字节数组中
        is.read(bytes);
        //创建HttpHeaders对象设置响应头信息
        MultiValueMap<String, String> headers = new HttpHeaders();
        //设置要下载方式以及下载文件的名字
        headers.add("Content-Disposition","attachment;filename=1.jpg");
            //设置响应状态码
        HttpStatus statusCode = HttpStatus.OK;
        //创建ResponseEntity对象
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, headers,statusCode);
        //关闭输入流
        is.close();
        return responseEntity;
    }

Five, SpringMVC execution process


因为我们有两种开发模式,我们分别来讲解两种模式在SpringMVC中的执行流程。

一种是类似JSP的开发流程:

				 把数据放入域对象中,然后进行页面跳转。

另外一种是前后端分离的开发模式,这也是目前市场上主流的模式:

				 把数据转化为json放入响应体中。

insert image description here

5.1 Execution process of development mode without separation of front and back ends

  1. User-initiated requests are processed by DispatchServlet

  2. DispatchServlet finds the Handler that can handle this request according to the specific request through HandlerMapping. (HandlerMapping mainly deals with the mapping relationship between requests and Handler methods)

  3. HandlerMapping returns an execution chain capable of processing requests to DispatchServlet, which includes not only Handler methods but also interceptors.

  4. DispatchServlet takes the execution chain to find the method in the HandlerAdater execution chain.

  5. HandlerAdater will execute the corresponding Handler method, convert the data processing into an appropriate type and pass it in as a method parameter

  6. The return value after the Handler method is executed will be converted to the ModelAndView type by the HandlerAdapter. (HandlerAdater mainly processes Handler method parameters and return values.)

  7. Return ModelAndView to DispatchServlet.

  8. If the corresponding ModelAndView object is not null, then DispatchServlet hands the ModelAndView to the ViewResolver, which is the view resolver for resolution.

  9. ViewResolver is the view resolver that converts the viewName in ModelAndView into the corresponding View object and returns it to DispatchServlet. (ViewResolver is mainly responsible for converting the viewName of String type into the corresponding View object)

  10. DispatchServlet uses the View object to display the page.

5.2 Execution process of front-end and back-end separation development mode

In the development mode of front-end and back-end separation, we will use @ResponseBody to write data into the response body. So there is no need to jump to the page.

So the process is as follows:

  1. User-initiated requests are processed by DispatchServlet

  2. DispatchServlet finds the Handler that can handle this request according to the specific request through HandlerMapping. (HandlerMapping mainly deals with the mapping relationship between requests and Handler methods)

  3. HandlerMapping returns an execution chain capable of processing requests to DispatchServlet, which includes not only Handler methods but also interceptors.

  4. DispatchServlet takes the execution chain to find the method in the HandlerAdater execution chain.

  5. HandlerAdater will execute the corresponding Handler method, convert the data processing into an appropriate type and pass it in as a method parameter

  6. The return value after the Handler method is executed will be converted to the ModelAndView type by the HandlerAdapter. Due to the @ResponseBody annotation, the returned ModelAndView will be null, and the HandlerAdapter will put the method return value into the response body. (HandlerAdater mainly processes Handler method parameters and return values.)

  7. Return ModelAndView to DispatchServlet.

  8. Because the returned ModelAndView is null, there is no need to parse the view parsing and subsequent operations.

Guess you like

Origin blog.csdn.net/lx00000025/article/details/132148930