(2) Spring MVC

3. @RequestMapping annotation**

1. Function of @RequestMapping annotation

From the annotation name, we can see that the function of the @RequestMapping annotation is to associate the request with the controller method that processes the request and establish a mapping relationship.

When SpringMVC receives the specified request, it will find the corresponding controller method in the mapping relationship to process the request.

2. The location of the @RequestMapping annotation

@RequestMapping identifies a class: sets the initial information of the request path of the mapping request

@RequestMapping identifies a method: set the specific information of the mapping request request path

@Controller
@RequestMapping("/test")
public class RequestMappingController {
    
    

	//此时请求映射所映射的请求的请求路径为:/test/testRequestMapping
    @RequestMapping("/testRequestMapping")
    public String testRequestMapping(){
    
    
        return "success";
    }

}

3. The value attribute of @RequestMapping annotation

The value attribute of the @RequestMapping annotation matches the request mapping through the request address of the request

The value attribute of the @RequestMapping annotation is an array of string types, indicating that the request mapping can match requests corresponding to multiple request addresses

The value attribute of the @RequestMapping annotation must be set, at least matching the request mapping through the request address

<a th:href="@{/testRequestMapping}">测试@RequestMapping的value属性-->/testRequestMapping</a><br>
<a th:href="@{/test}">测试@RequestMapping的value属性-->/test</a><br>
@RequestMapping(
        value = {"/testRequestMapping", "/test"}
)
public String testRequestMapping(){
    return "success";
}

4. The method attribute of @RequestMapping annotation

The method attribute of the @RequestMapping annotation matches the request mapping by the request method (get or post) of the request

The method attribute of the @RequestMapping annotation is an array of RequestMethod type, indicating that the request mapping can match requests in multiple request methods

If the request address of the current request satisfies the value attribute of the request mapping, but the request method does not satisfy the method attribute, the browser will report an error 405: Request method 'POST' not supported

<a th:href="@{/test}">测试@RequestMapping的value属性-->/test</a><br>
<form th:action="@{/test}" method="post">
    <input type="submit">
</form>
@RequestMapping(
        value = {
    
    "/testRequestMapping", "/test"},
        method = {
    
    RequestMethod.GET, RequestMethod.POST}
)
public String testRequestMapping(){
    
    
    return "success";
}
注:

1、对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解

处理get请求的映射–>@GetMapping

处理post请求的映射–>@PostMapping

处理put请求的映射–>@PutMapping

处理delete请求的映射–>@DeleteMapping

2、常用的请求方式有get,post,put,delete

但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理

若要发送put和delete请求,则需要通过spring提供的过滤器HiddenHttpMethodFilter,在RESTful部分会讲到

5. The params attribute of @RequestMapping annotation (understand)

The params attribute of the @RequestMapping annotation matches the request mapping through the request parameters of the request

The params attribute of the @RequestMapping annotation is an array of string types, and the matching relationship between request parameters and request mapping can be set through four expressions

"param": The request matched by the request mapping must carry the param request parameter

"!param": The request matched by the request mapping must not carry the param request parameter

"param=value": The request matched by the request mapping must carry the param request parameter and param=value

"param!=value": Requests matched by the request mapping must carry param request parameters but param!=value

<a th:href="@{/test(username='admin',password=123456)">测试@RequestMapping的params属性-->/test</a><br>

@RequestMapping(
        value = {
    
    "/testRequestMapping", "/test"}
        ,method = {
    
    RequestMethod.GET, RequestMethod.POST}
        ,params = {
    
    "username","password!=123456"}
)
public String testRequestMapping(){
    
    
    return "success";
}
注:

若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时页面回报错400:Parameter conditions “username, password!=123456” not met for actual request parameters: username={admin}, password={123456}

6. The headers attribute of the @RequestMapping annotation (understand)

The headers attribute of the @RequestMapping annotation matches the request mapping through the request header information of the request

The headers attribute of the @RequestMapping annotation is an array of string types, and the matching relationship between request header information and request mapping can be set through four expressions

"header": The request matched by the request mapping must carry the header request header information

"!header": The request matched by the request mapping must not carry the header request header information

"header=value": The request matched by the request mapping must carry header request header information and header=value

"header!=value": The request matched by the request mapping must carry header request header information and header!=value

If the current request satisfies the value and method attributes of the @RequestMapping annotation, but does not satisfy the headers attribute, the page displays a 404 error, that is, the resource is not found

7. SpringMVC supports ant-style paths

? : represents any single character

*: Indicates any 0 or more characters

**: Indicates any one or more layers of directories

Note: When using , you can only use // xxx

8. SpringMVC supports placeholders in the path (emphasis)

Original method: /deleteUser?id=1

Rest method: /deleteUser/1

The placeholders in the SpringMVC path are often used in the RESTful style. When some data is transmitted to the server through the path in the request path, it can be represented by the placeholder {xxx} in the value attribute of the corresponding @RequestMapping annotation For the transmitted data, assign the data represented by the placeholder to the formal parameter of the controller method through the @PathVariable annotation

<a th:href="@{/testRest/1/admin}">测试路径中的占位符-->/testRest</a><br>
@RequestMapping("/testRest/{id}/{username}")
public String testRest(@PathVariable("id") String id, @PathVariable("username") String username){
    
    
    System.out.println("id:"+id+",username:"+username);
    return "success";
}
//最终输出的内容为-->id:1,username:admin

Fourth, SpringMVC obtains request parameters

1. Get it through Servlet API

Use HttpServletRequest as the formal parameter of the controller method. At this time, the parameter of type HttpServletRequest represents the object that encapsulates the request message of the current request

@RequestMapping("/testParam")
public String testParam(HttpServletRequest request){
    
    
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    System.out.println("username:"+username+",password:"+password);
    return "success";
}

2. Obtain request parameters through the formal parameters of the controller method

In the formal parameter position of the controller method, set the formal parameter with the same name as the request parameter. When the browser sends a request and matches the request mapping, the request parameter will be assigned to the corresponding formal parameter in DispatcherServlet

<a th:href="@{/testParam(username='admin',password=123456)}">测试获取请求参数-->/testParam</a><br>

@RequestMapping("/testParam")
public String testParam(String username, String password){
    
    
    System.out.println("username:"+username+",password:"+password);
    return "success";
}

Note:

If there are multiple request parameters with the same name in the request parameters transmitted by the request, you can set a string array or a string-type parameter in the formal parameters of the controller method to receive the request parameters

If a formal parameter of type string array is used, the array of this parameter contains each data

If a formal parameter of string type is used, the value of this parameter is the result of splicing each data with commas

3、@RequestParam

@RequestParam is to create a mapping relationship between request parameters and formal parameters of controller methods

The @RequestParam annotation has three attributes:

value: specifies the parameter name of the request parameter assigned as the formal parameter

required: Set whether this request parameter must be transmitted, the default value is true

If it is set to true, the current request must transmit the request parameter specified by value. If the request parameter is not transmitted and the defaultValue attribute is not set, the page will report an error 400: Required String parameter 'xxx' is not present; if it is set to false , the current request does not have to transmit the request parameter specified by value, if not transmitted, the value of the formal parameter identified by the annotation is null

defaultValue: Regardless of the value of the required attribute is true or false, when the request parameter specified by value is not transmitted or the transmitted value is "", the default value is used to assign the formal parameter

4、@RequestHeader

@RequestHeader creates a mapping relationship between the request header information and the formal parameters of the controller method

The @RequestHeader annotation has three attributes: value, required, defaultValue, the usage is the same as @RequestParam

5、@CookieValue

@CookieValue is to create a mapping relationship between cookie data and controller method parameters

The @CookieValue annotation has three attributes: value, required, defaultValue, the usage is the same as @RequestParam

6. Obtain request parameters through POJO

You can set a formal parameter of entity class type in the formal parameter position of the controller method. At this time, if the parameter name of the request parameter transmitted by the browser is consistent with the attribute name in the entity class, then the request parameter will assign a value to this attribute

<form th:action="@{/testpojo}" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    性别:<input type="radio" name="sex" value=""><input type="radio" name="sex" value=""><br>
    年龄:<input type="text" name="age"><br>
    邮箱:<input type="text" name="email"><br>
    <input type="submit">
</form>

@RequestMapping("/testpojo")
public String testPOJO(User user){
    
    
    System.out.println(user);
    return "success";
}
//最终结果-->User{id=null, username='张三', password='123', age=23, sex='男', email='[email protected]'}

7. Solve the problem of garbled characters in obtaining request parameters

To solve the garbled problem of obtaining request parameters, you can use the encoding filter CharacterEncodingFilter provided by SpringMVC, but it must be registered in web.xml

<!--配置springMVC的编码过滤器-->
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Note:

The filter that handles encoding in SpringMVC must be configured before other filters, otherwise it will be invalid

5. Domain objects share data

1. Use ServletAPI to share data with request domain objects

@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request){
    
    
    request.setAttribute("testScope", "hello,servletAPI");
    return "success";
}

2. Use ModelAndView to share data with request domain objects (recommended)

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
    
    
    /**
     * ModelAndView有Model和View的功能
     * Model主要用于向请求域共享数据
     * View主要用于设置视图,实现页面跳转
     */
    ModelAndView mav = new ModelAndView();
    //向请求域共享数据
    mav.addObject("testScope", "hello,ModelAndView");
    //设置视图,实现页面跳转
    mav.setViewName("success");
    return mav;
}

3. Use Model to share data with request domain objects

@RequestMapping("/testModel")
public String testModel(Model model){
    
    
    model.addAttribute("testScope", "hello,Model");
    return "success";
}

4. Use map to share data with request domain objects

@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
    
    
    map.put("testScope", "hello,Map");
    return "success";
}

5. Use ModelMap to share data with request domain objects

@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
    
    
    modelMap.addAttribute("testScope", "hello,ModelMap");
    return "success";
}

6. Relationship between Model, ModelMap and Map

The parameters of the Model, ModelMap, and Map types are actually of the BindingAwareModelMap type

public interface Model{
    
    }
public class ModelMap extends LinkedHashMap<String, Object> {
    
    }
public class ExtendedModelMap extends ModelMap implements Model {
    
    }
public class BindingAwareModelMap extends ExtendedModelMap {
    
    }

7. Share data with the session domain

@RequestMapping("/testSession")
public String testSession(HttpSession session){
    
    
    session.setAttribute("testSessionScope", "hello,session");
    return "success";
}

8. Share data with the application domain

@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
    
    
	ServletContext application = session.getServletContext();
    application.setAttribute("testApplicationScope", "hello,application");
    return "success";
}

6. View of SpringMVC

The view in SpringMVC is the View interface. The function of the view is to render data and display the data in the model to the user.

There are many types of SpringMVC views, and there are forwarding views and redirecting views by default.

When the project introduces jstl dependencies, the forwarding view will be automatically converted to JstlView

If the view technology used is Thymeleaf, the Thymeleaf view parser is configured in the SpringMVC configuration file, and the ThymeleafView is obtained after parsing the view parser

1、ThymeleafView

When the view name set in the controller method does not have any prefix, the view name at this time will be parsed by the view resolver configured in the SpringMVC configuration file, and the final path obtained by splicing the view name with the view prefix and view suffix will pass The forwarding method realizes the jump

@RequestMapping("/testHello")
public String testHello(){
    
    
    return "hello";
}

2. Forward view

The default forwarding view in SpringMVC is InternalResourceView

The situation of creating forwarding view in SpringMVC:

When the view name set in the controller method is prefixed with "forward:", the InternalResourceView view is created. The view name at this time will not be parsed by the view resolver configured in the SpringMVC configuration file, but will be prefixed with "forward :"Removed, the remaining part is used as the final path to realize the jump by forwarding

For example "forward:/", "forward:/employee"

@RequestMapping("/testForward")
public String testForward(){
    
    
    return "forward:/testHello";
}

3. Redirect view

The default redirection view in SpringMVC is RedirectView

When the view name set in the controller method is prefixed with "redirect:", the RedirectView view is created. The view name at this time will not be parsed by the view resolver configured in the SpringMVC configuration file, but will be prefixed with "redirect :"Removed, the remaining part is used as the final path to realize the jump through redirection

For example "redirect:/", "redirect:/employee"

@RequestMapping("/testRedirect")
public String testRedirect(){
    
    
    return "redirect:/testHello";
}

Note:

When parsing the redirected view, the redirect: prefix will be removed first, and then it will be judged whether the remaining part starts with /, and if so, the context path will be automatically spliced

4. View controller view-controller

When the controller method is only used to realize page jump, that is, when only the view name needs to be set, the processor method can be represented by the view-controller tag

<!--
	path:设置处理的请求地址
	view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>

Note:

When any view-controller is set in SpringMVC, the request mapping in other controllers will all be invalid. At this time, it is necessary to set the tag to enable the mvc annotation driver in the core configuration file of SpringMVC:

<mvc:annotation-driven />

Seven, RESTful

1. Introduction to RESTful

REST: Re presentational State Transfer , resource state transfer at the presentation layer .

a> resources

A resource is a way of looking at a server, that is, as consisting of many discrete resources. Each resource is a nameable abstraction on the server. Because a resource is an abstract concept, it can not only represent a file in the server file system, a table in the database, and other specific things. You can design resources as abstract as your imagination allows. And client application developers can understand. Similar to object-oriented design, resources are organized around nouns, with the nouns being the first focus. A resource can be identified by one or more URIs. A URI is both the name of a resource and its address on the Web. A client application interested in a resource can interact with it through the resource's URI.

b>Representation of resources

A resource representation is a description of the state of the resource at a particular moment. Can be transferred (swapped) between client-server. Resource representations can be in multiple formats, such as HTML/XML/JSON/plain text/picture/video/audio and so on. The representation format of resources can be determined through a negotiation mechanism. The representation of the request-response direction usually uses a different format.

c> state transition

State transfer refers to the transfer (transfer) between the client and the server to represent the representation of the state of the resource. Through the expression of transferring and operating resources, the purpose of operating resources is indirectly realized.

2. Implementation of RESTful

Specifically, in the HTTP protocol, there are four verbs expressing operation methods: GET, POST, PUT, and DELETE.

They correspond to four basic operations: GET is used to obtain resources, POST is used to create new resources, PUT is used to update resources, and DELETE is used to delete resources.

The REST style advocates the use of a unified style design for the URL address. Each word is separated by a slash from front to back, and the request parameters are not carried in the form of question mark key-value pairs. Instead, the data to be sent to the server is used as a part of the URL address to ensure the overall style. consistency.

operate traditional way REST style
query operation getUserById?id=1 user/1–>get request method
save operation saveUser user–>post request method
delete operation deleteUser?id=1 user/1–>delete request method
update operation updateUser user–>put request method

3、HiddenHttpMethodFilter

Since the browser only supports sending get and post requests, how to send put and delete requests?

SpringMVC provides HiddenHttpMethodFilter to help us convert POST requests into DELETE or PUT requests

HiddenHttpMethodFilter handles the conditions for put and delete requests:

a> The request method of the current request must be post

b>The current request must transmit the request parameter _method

If the above conditions are met, the HiddenHttpMethodFilter filter will convert the request method of the current request into the value of the request parameter _method, so the value of the request parameter _method is the final request method

Register HiddenHttpMethodFilter in web.xml

<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Note:

So far, two filters are provided in SpringMVC: CharacterEncodingFilter and HiddenHttpMethodFilter

When registering in web.xml, you must first register CharacterEncodingFilter, and then register HiddenHttpMethodFilter

reason:

  • In CharacterEncodingFilter, set the character set through the request.setCharacterEncoding(encoding) method
  • The request.setCharacterEncoding(encoding) method requires no previous operations to obtain request parameters
  • And HiddenHttpMethodFilter has exactly one operation to get the request method:
String paramValue = request.getParameter(this.methodParam);

8. RESTful case

1. Preparation

Like traditional CRUD, it can add, delete, modify and check employee information.

  • build environment

  • Prepare Entity Class

    package com.atguigu.mvc.bean;
    
    public class Employee {
          
          
    
       private Integer id;
       private String lastName;
    
       private String email;
       //1 male, 0 female
       private Integer gender;
       
       public Integer getId() {
          
          
          return id;
       }
    
       public void setId(Integer id) {
          
          
          this.id = id;
       }
    
       public String getLastName() {
          
          
          return lastName;
       }
    
       public void setLastName(String lastName) {
          
          
          this.lastName = lastName;
       }
    
       public String getEmail() {
          
          
          return email;
       }
    
       public void setEmail(String email) {
          
          
          this.email = email;
       }
    
       public Integer getGender() {
          
          
          return gender;
       }
    
       public void setGender(Integer gender) {
          
          
          this.gender = gender;
       }
    
       public Employee(Integer id, String lastName, String email, Integer gender) {
          
          
          super();
          this.id = id;
          this.lastName = lastName;
          this.email = email;
          this.gender = gender;
       }
    
       public Employee() {
          
          
       }
    }
    
    

    Prepare dao simulation data

    package com.atguigu.mvc.dao;
    
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    
    import com.atguigu.mvc.bean.Employee;
    import org.springframework.stereotype.Repository;
    
    
    @Repository
    public class EmployeeDao {
          
          
    
       private static Map<Integer, Employee> employees = null;
       
       static{
          
          
          employees = new HashMap<Integer, Employee>();
    
          employees.put(1001, new Employee(1001, "E-AA", "[email protected]", 1));
          employees.put(1002, new Employee(1002, "E-BB", "[email protected]", 1));
          employees.put(1003, new Employee(1003, "E-CC", "[email protected]", 0));
          employees.put(1004, new Employee(1004, "E-DD", "[email protected]", 0));
          employees.put(1005, new Employee(1005, "E-EE", "[email protected]", 1));
       }
       
       private static Integer initId = 1006;
       
       public void save(Employee employee){
          
          
          if(employee.getId() == null){
          
          
             employee.setId(initId++);
          }
          employees.put(employee.getId(), employee);
       }
       
       public Collection<Employee> getAll(){
          
          
          return employees.values();
       }
       
       public Employee get(Integer id){
          
          
          return employees.get(id);
       }
       
       public void delete(Integer id){
          
          
          employees.remove(id);
       }
    }
    
    

    2. List of functions

    Function URL address request method
    Visit homepage√ / GET
    Query all data√ /employee GET
    Delete √ /employee/2 DELETE
    Jump to add data page√ /toAdd GET
    Execute save√ /employee POST
    Jump to update data page√ /employee/2 GET
    Perform update√ /employee PUT

3. Specific function: visit the home page

a> configure view-controller
<mvc:view-controller path="/" view-name="index"/>
b> create page
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" >
    <title>Title</title>
</head>
<body>
<h1>首页</h1>
<a th:href="@{/employee}">访问员工信息</a>
</body>
</html>

4. Specific function: query all employee data

a> controller method
@RequestMapping(value = "/employee", method = RequestMethod.GET)
public String getEmployeeList(Model model){
    
    
    Collection<Employee> employeeList = employeeDao.getAll();
    model.addAttribute("employeeList", employeeList);
    return "employee_list";
}

b> create employee_list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Employee Info</title>
    <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
</head>
<body>

    <table border="1" cellpadding="0" cellspacing="0" style="text-align: center;" id="dataTable">
        <tr>
            <th colspan="5">Employee Info</th>
        </tr>
        <tr>
            <th>id</th>
            <th>lastName</th>
            <th>email</th>
            <th>gender</th>
            <th>options(<a th:href="@{/toAdd}">add</a>)</th>
        </tr>
        <tr th:each="employee : ${employeeList}">
            <td th:text="${employee.id}"></td>
            <td th:text="${employee.lastName}"></td>
            <td th:text="${employee.email}"></td>
            <td th:text="${employee.gender}"></td>
            <td>
                <a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
                <a th:href="@{'/employee/'+${employee.id}}">update</a>
            </td>
        </tr>
    </table>
</body>
</html>

5. Specific function: delete

a> Create a form that handles delete requests
<!-- 作用:通过超链接控制表单的提交,将post请求转换为delete请求 -->
<form id="delete_form" method="post">
    <!-- HiddenHttpMethodFilter要求:必须传输_method请求参数,并且值为最终的请求方式 -->
    <input type="hidden" name="_method" value="delete"/>
</form>

b> Delete hyperlink binding click event

Introduce vue.js

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>

delete hyperlink

<a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>

Handle click events through vue

<script type="text/javascript">
    var vue = new Vue({
      
      
        el:"#dataTable",
        methods:{
      
      
            //event表示当前事件
            deleteEmployee:function (event) {
      
      
                //通过id获取表单标签
                var delete_form = document.getElementById("delete_form");
                //将触发事件的超链接的href属性为表单的action属性赋值
                delete_form.action = event.target.href;
                //提交表单
                delete_form.submit();
                //阻止超链接的默认跳转行为
                event.preventDefault();
            }
        }
    });
</script>

c> controller method
<mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>

6. Specific function: Jump to the page of adding data

a> configure view-controller
<mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>

b> create employee_add.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Add Employee</title>
</head>
<body>

<form th:action="@{/employee}" method="post">
    lastName:<input type="text" name="lastName"><br>
    email:<input type="text" name="email"><br>
    gender:<input type="radio" name="gender" value="1">male
    <input type="radio" name="gender" value="0">female<br>
    <input type="submit" value="add"><br>
</form>

</body>
</html>

7. Specific function: execute save

a> controller method
@RequestMapping(value = "/employee", method = RequestMethod.POST)
public String addEmployee(Employee employee){
    
    
    employeeDao.save(employee);
    return "redirect:/employee";
}

8. Specific function: jump to update data page

a> modify hyperlink
<a th:href="@{'/employee/'+${employee.id}}">update</a>
b> controller method
@RequestMapping(value = "/employee/{id}", method = RequestMethod.GET)
public String getEmployeeById(@PathVariable("id") Integer id, Model model){
    
    
    Employee employee = employeeDao.get(id);
    model.addAttribute("employee", employee);
    return "employee_update";
}

c> create employee_update.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Update Employee</title>
</head>
<body>

<form th:action="@{/employee}" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="hidden" name="id" th:value="${employee.id}">
    lastName:<input type="text" name="lastName" th:value="${employee.lastName}"><br>
    email:<input type="text" name="email" th:value="${employee.email}"><br>
    <!--
        th:field="${employee.gender}"可用于单选框或复选框的回显
        若单选框的value和employee.gender的值一致,则添加checked="checked"属性
    -->
    gender:<input type="radio" name="gender" value="1" th:field="${employee.gender}">male
    <input type="radio" name="gender" value="0" th:field="${employee.gender}">female<br>
    <input type="submit" value="update"><br>
</form>

</body>
</html>

9. Specific function: perform update

a> controller method
@RequestMapping(value = "/employee", method = RequestMethod.PUT)
public String updateEmployee(Employee employee){
    
    
    employeeDao.save(employee);
    return "redirect:/employee";
}

八、HttpMessageConverter

HttpMessageConverter, a message information converter, converts a request message into a Java object, or converts a Java object into a response message

HttpMessageConverter provides two annotations and two types: @RequestBody, @ResponseBody, RequestEntity,

ResponseEntity

1、@RequestBody

@RequestBody can get the request body, you need to set a formal parameter in the controller method, use @RequestBody to identify, the request body of the current request will assign a value to the formal parameter identified by the current annotation

<form th:action="@{/testRequestBody}" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit">
</form>

@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
    
    
    System.out.println("requestBody:"+requestBody);
    return "success";
}

Output result:

requestBody:username=admin&password=123456

2、RequestEntity

RequestEntity encapsulates a type of request message, which needs to be set in the formal parameter of the controller method, and the request message of the current request will be assigned to the formal parameter, and the request header information can be obtained through getHeaders(), Obtain request body information through getBody()

@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
    
    
    System.out.println("requestHeader:"+requestEntity.getHeaders());
    System.out.println("requestBody:"+requestEntity.getBody());
    return "success";
}

输出结果:
requestHeader:[host:“localhost:8080”, connection:“keep-alive”, content-length:“27”, cache-control:“max-age=0”, sec-ch-ua:“” Not A;Brand";v=“99”, “Chromium”;v=“90”, “Google Chrome”;v=“90"”, sec-ch-ua-mobile:“?0”, upgrade-insecure-requests:“1”, origin:“http://localhost:8080”, user-agent:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36”]
requestBody:username=admin&password=123

3、@ResponseBody

@ResponseBody is used to identify a controller method, and the return value of the method can be directly responded to the browser as the response body of the response message

@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
    
    
    return "success";
}

Result: The browser page displays success

4. Spring MVC processes json

Steps for @ResponseBody to process json:

a> import jackson dependencies

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>

b> Turn on the annotation driver of mvc in the core configuration file of SpringMVC. At this time, a message converter will be automatically assembled in the HandlerAdaptor: MappingJackson2HttpMessageConverter, which can convert the Java object responding to the browser into a string in Json format

<mvc:annotation-driven />

c> Use the @ResponseBody annotation on the processor method to identify

d> Return the Java object directly as the return value of the controller method, and it will be automatically converted to a string in Json format

@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){
    
    
    return new User(1001,"admin","123456",23,"男");
}

The results displayed in the browser's page:

{“id”:1001,“username”:“admin”,“password”:“123456”,“age”:23,“sex”:“男”}

5. Spring MVC handles ajax

a> request hyperlink

<div id="app">
	<a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br>
</div>

b> Handle click events through vue and axios:

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript">
    var vue = new Vue({
      
      
        el:"#app",
        methods:{
      
      
            testAjax:function (event) {
      
      
                axios({
      
      
                    method:"post",
                    url:event.target.href,
                    params:{
      
      
                        username:"admin",
                        password:"123456"
                    }
                }).then(function (response) {
      
      
                    alert(response.data);
                });
                event.preventDefault();
            }
        }
    });
</script>

c>Controller method:

@RequestMapping("/testAjax")
@ResponseBody
public String testAjax(String username, String password){
    
    
    System.out.println("username:"+username+",password:"+password);
    return "hello,ajax";
}

6. @RestController annotation

The @RestController annotation is a composite annotation provided by springMVC. It is marked on the controller class, which is equivalent to adding the @Controller annotation to the class, and adding the @ResponseBody annotation to each method in it.

7、ResponseEntity

ResponseEntity is used for the return value type of the controller method, and the return value of the controller method is the response message to the browser

9. File upload and download

1. File download

Use ResponseEntity to realize the function of downloading files

@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
    
    
    //获取ServletContext对象
    ServletContext servletContext = session.getServletContext();
    //获取服务器中文件的真实路径
    String realPath = servletContext.getRealPath("/static/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<>(bytes, headers, statusCode);
    //关闭输入流
    is.close();
    return responseEntity;
}

123456789101112131415161718192021222324

2. File upload

File upload requires that the request method of the form form must be post, and add the attribute enctype="multipart/form-data"

In SpringMVC, the uploaded file is encapsulated into a MultipartFile object, through which file-related information can be obtained

Upload steps:

a> Add dependencies:

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
123456

b>Add configuration to SpringMVC configuration file:

<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
12

c>Controller method:

@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
    
    
    //获取上传的文件的文件名
    String fileName = photo.getOriginalFilename();
    //处理文件重名问题
    String hzName = fileName.substring(fileName.lastIndexOf("."));
    fileName = UUID.randomUUID().toString() + hzName;
    //获取服务器中photo目录的路径
    ServletContext servletContext = session.getServletContext();
    String photoPath = servletContext.getRealPath("photo");
    File file = new File(photoPath);
    if(!file.exists()){
    
    
        file.mkdir();
    }
    String finalPath = photoPath + File.separator + fileName;
    //实现上传功能
    photo.transferTo(new File(finalPath));
    return "success";
}

12345678910111213141516171819

10. Interceptor

1. Interceptor configuration

Interceptors in SpringMVC are used to intercept the execution of controller methods

Interceptors in SpringMVC need to implement HandlerInterceptor

The SpringMVC interceptor must be configured in the SpringMVC configuration file:

<bean class="com.atguigu.interceptor.FirstInterceptor"></bean>
<ref bean="firstInterceptor"></ref>
<!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 -->
<mvc:interceptor>
    <mvc:mapping path="/**"/>
    <mvc:exclude-mapping path="/testRequestEntity"/>
    <ref bean="firstInterceptor"></ref>
</mvc:interceptor>
<!-- 
	以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
-->
1234567891011

2. Three abstract methods of interceptors

Interceptors in SpringMVC have three abstract methods:

preHandle: Execute preHandle() before the execution of the controller method. Its return value of boolean type indicates whether to intercept or release, return true to release, that is, call the controller method; return false to indicate interception, that is, not to call the controller method

postHandle: Execute postHandle() after the controller method is executed

afterComplation: After processing the view and model data, execute afterComplation() after rendering the view

3. Execution order of multiple interceptors

a>If preHandle() of each interceptor returns true

At this time, the execution order of multiple interceptors is related to the configuration order of the interceptors in the SpringMVC configuration file:

preHandle() will be executed in the order of configuration, while postHandle() and afterComplation() will be executed in reverse order of configuration

b> If the preHandle() of an interceptor returns false

preHandle() returns false and preHandle() of its previous interceptor will be executed, postHandle() will not be executed, and afterComplation() of the interceptor before the interceptor returning false will be executed

Eleven, exception handler

1. Configuration-based exception handling

SpringMVC provides an interface to handle exceptions that occur during the execution of controller methods: HandlerExceptionResolver

The implementation classes of the HandlerExceptionResolver interface are: DefaultHandlerExceptionResolver and SimpleMappingExceptionResolver

SpringMVC provides a custom exception handler SimpleMappingExceptionResolver, used:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
        	<!--
        		properties的键表示处理器方法执行过程中出现的异常
        		properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
        	-->
            <prop key="java.lang.ArithmeticException">error</prop>
        </props>
    </property>
    <!--
    	exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
    -->
    <property name="exceptionAttribute" value="ex"></property>
</bean>
123456789101112131415

2. Annotation-based exception handling

//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
    
    

    //@ExceptionHandler用于设置所标识方法处理的异常
    @ExceptionHandler(ArithmeticException.class)
    //ex表示当前请求处理中出现的异常对象
    public String handleArithmeticException(Exception ex, Model model){
    
    
        model.addAttribute("ex", ex);
        return "error";
    }

}
12345678910111213

12. Annotation configuration SpringMVC

Use configuration classes and annotations to replace the functions of web.xml and SpringMVC configuration files

1. Create an initialization class to replace web.xml

In the Servlet3.0 environment, the container will look for a class that implements the javax.servlet.ServletContainerInitializer interface in the class path, and if found, use it to configure the Servlet container.
Spring provides an implementation of this interface, named SpringServletContainerInitializer, which in turn looks for classes that implement WebApplicationInitializer and delegates the configuration tasks to them. Spring 3.2 introduces a convenient basic implementation of WebApplicationInitializer named AbstractAnnotationConfigDispatcherServletInitializer. When our class extends AbstractAnnotationConfigDispatcherServletInitializer and deploys it to a Servlet3.0 container, the container will automatically discover it and use it to configure the Servlet context.

public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    

    /**
     * 指定spring的配置类
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
    
    
        return new Class[]{
    
    SpringConfig.class};
    }

    /**
     * 指定SpringMVC的配置类
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
    
    
        return new Class[]{
    
    WebConfig.class};
    }

    /**
     * 指定DispatcherServlet的映射规则,即url-pattern
     * @return
     */
    @Override
    protected String[] getServletMappings() {
    
    
        return new String[]{
    
    "/"};
    }

    /**
     * 添加过滤器
     * @return
     */
    @Override
    protected Filter[] getServletFilters() {
    
    
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceRequestEncoding(true);
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        return new Filter[]{
    
    encodingFilter, hiddenHttpMethodFilter};
    }
}

123456789101112131415161718192021222324252627282930313233343536373839404142

2. Create a SpringConfig configuration class to replace the spring configuration file

@Configuration
public class SpringConfig {
    
    
	//ssm整合之后,spring的配置信息写在此类中
}
1234

3. Create a WebConfig configuration class to replace the SpringMVC configuration file

@Configuration
//扫描组件
@ComponentScan("com.atguigu.mvc.controller")
//开启MVC注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    

    //使用默认的servlet处理静态资源
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    
    
        configurer.enable();
    }

    //配置文件上传解析器
    @Bean
    public CommonsMultipartResolver multipartResolver(){
    
    
        return new CommonsMultipartResolver();
    }

    //配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        FirstInterceptor firstInterceptor = new FirstInterceptor();
        registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
    }
    
    //配置视图控制
    
    /*@Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }*/
    
    //配置异常映射
    /*@Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
        Properties prop = new Properties();
        prop.setProperty("java.lang.ArithmeticException", "error");
        //设置异常映射
        exceptionResolver.setExceptionMappings(prop);
        //设置共享异常信息的键
        exceptionResolver.setExceptionAttribute("ex");
        resolvers.add(exceptionResolver);
    }*/

    //配置生成模板解析器
    @Bean
    public ITemplateResolver templateResolver() {
    
    
        WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
                webApplicationContext.getServletContext());
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    }

    //生成模板引擎并为模板引擎注入模板解析器
    @Bean
    public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
    
    
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        return templateEngine;
    }

    //生成视图解析器并未解析器注入模板引擎
    @Bean
    public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
    
    
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setCharacterEncoding("UTF-8");
        viewResolver.setTemplateEngine(templateEngine);
        return viewResolver;
    }


}

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879

4. Test function

@RequestMapping("/")
public String index(){
    
    
    return "index";
}
1234

Thirteen, SpringMVC execution process

1. Common components of SpringMVC

  • DispatcherServlet: front-end controller , does not require engineers to develop, provided by the framework

Role: Unified processing of requests and responses, the center of the entire process control, which calls other components to process user requests

  • HandlerMapping: processor mapper , does not require engineers to develop, provided by the framework

Function: Find the Handler according to the requested url, method and other information, that is, the controller method

  • Handler: Processor , which needs to be developed by engineers

Function: Handler processes specific user requests under the control of DispatcherServlet

  • HandlerAdapter: Processor adapter , no need for engineer development, provided by the framework

Function: Execute the processor (controller method) through HandlerAdapter

  • ViewResolver: view resolver , no need for engineer development, provided by the framework

Function: Perform view analysis to obtain corresponding views, for example: ThymeleafView, InternalResourceView, RedirectView

  • View: view

Role: display the model data to the user through the page

2. DispatcherServlet initialization process

DispatcherServlet is essentially a Servlet, so it naturally follows the Servlet life cycle. So macroscopically, it is the Servlet life cycle to schedule.

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-Set354Cp-1628192174721)(img/img005.png)]

a>Initialize the WebApplicationContext

Class: org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext initWebApplicationContext() {
    
    
    WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (this.webApplicationContext != null) {
    
    
        // A context instance was injected at construction time -> use it
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
    
    
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
    
    
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
    
    
                    // The context instance was injected without an explicit parent -> set
                    // the root application context (if any; may be null) as the parent
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
    
    
        // No context instance was injected at construction time -> see if one
        // has been registered in the servlet context. If one exists, it is assumed
        // that the parent context (if any) has already been set and that the
        // user has performed any initialization such as setting the context id
        wac = findWebApplicationContext();
    }
    if (wac == null) {
    
    
        // No context instance is defined for this servlet -> create a local one
        // 创建WebApplicationContext
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
    
    
        // Either the context is not a ConfigurableApplicationContext with refresh
        // support or the context injected at construction time had already been
        // refreshed -> trigger initial onRefresh manually here.
        synchronized (this.onRefreshMonitor) {
    
    
            // 刷新WebApplicationContext
            onRefresh(wac);
        }
    }

    if (this.publishContext) {
    
    
        // Publish the context as a servlet context attribute.
        // 将IOC容器在应用域共享
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
b> create WebApplicationContext

Class: org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    
    
    Class<?> contextClass = getContextClass();
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
    
    
        throw new ApplicationContextException(
            "Fatal initialization error in servlet with name '" + getServletName() +
            "': custom WebApplicationContext class [" + contextClass.getName() +
            "] is not of type ConfigurableWebApplicationContext");
    }
    // 通过反射创建 IOC 容器对象
    ConfigurableWebApplicationContext wac =
        (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    // 设置父容器
    wac.setParent(parent);
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
    
    
        wac.setConfigLocation(configLocation);
    }
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

1234567891011121314151617181920212223
c>DispatcherServlet initialization strategy

After the FrameworkServlet creates the WebApplicationContext, it refreshes the container and calls onRefresh(wac). This method is rewritten in the DispatcherServlet, and the initStrategies(context) method is called to initialize the strategy, that is, to initialize each component of the DispatcherServlet.

Class: org.springframework.web.servlet.DispatcherServlet

protected void initStrategies(ApplicationContext context) {
    
    
   initMultipartResolver(context);
   initLocaleResolver(context);
   initThemeResolver(context);
   initHandlerMappings(context);
   initHandlerAdapters(context);
   initHandlerExceptionResolvers(context);
   initRequestToViewNameTranslator(context);
   initViewResolvers(context);
   initFlashMapManager(context);
}
1234567891011

3. DispatcherServlet calls the component to process the request

a>processRequest()

FrameworkServlet rewrites service() and doXxx() in HttpServlet, and processRequest(request, response) is called in these methods

Class: org.springframework.web.servlet.FrameworkServlet

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    
    

    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;

    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);

    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    initContextHolders(request, localeContext, requestAttributes);

    try {
    
    
		// 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写
        doService(request, response);
    }
    catch (ServletException | IOException ex) {
    
    
        failureCause = ex;
        throw ex;
    }
    catch (Throwable ex) {
    
    
        failureCause = ex;
        throw new NestedServletException("Request processing failed", ex);
    }

    finally {
    
    
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
    
    
            requestAttributes.requestCompleted();
        }
        logResult(request, response, failureCause, asyncManager);
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

123456789101112131415161718192021222324252627282930313233343536373839
b>doService()

Class: org.springframework.web.servlet.DispatcherServlet

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
    logRequest(request);

    // Keep a snapshot of the request attributes in case of an include,
    // to be able to restore the original attributes after the include.
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
    
    
        attributesSnapshot = new HashMap<>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
    
    
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
    
    
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    // Make framework objects available to handlers and view objects.
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

    if (this.flashMapManager != null) {
    
    
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
    
    
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    }

    RequestPath requestPath = null;
    if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
    
    
        requestPath = ServletRequestPathUtils.parseAndCache(request);
    }

    try {
    
    
        // 处理请求和响应
        doDispatch(request, response);
    }
    finally {
    
    
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    
    
            // Restore the original attribute snapshot, in case of an include.
            if (attributesSnapshot != null) {
    
    
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
        if (requestPath != null) {
    
    
            ServletRequestPathUtils.clearParsedRequestPath(request);
        }
    }
}

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
c>doDispatch()

Class: org.springframework.web.servlet.DispatcherServlet

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
    
    
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
    
    
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.
            /*
            	mappedHandler:调用链
                包含handler、interceptorList、interceptorIndex
            	handler:浏览器发送的请求所匹配的控制器方法
            	interceptorList:处理控制器方法的所有拦截器集合
            	interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行
            */
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
    
    
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.
           	// 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
    
    
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    
    
                    return;
                }
            }
			
            // 调用拦截器的preHandle()
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    
    
                return;
            }

            // Actually invoke the handler.
            // 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
    
    
                return;
            }

            applyDefaultViewName(processedRequest, mv);
            // 调用拦截器的postHandle()
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
    
    
            dispatchException = ex;
        }
        catch (Throwable err) {
    
    
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // 后续处理:处理模型数据和渲染视图
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
    
    
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
    
    
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                               new NestedServletException("Handler processing failed", err));
    }
    finally {
    
    
        if (asyncManager.isConcurrentHandlingStarted()) {
    
    
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
    
    
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
    
    
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
    
    
                cleanupMultipart(processedRequest);
            }
        }
    }
}

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
d>processDispatchResult()
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                   @Nullable Exception exception) throws Exception {
    
    

    boolean errorView = false;

    if (exception != null) {
    
    
        if (exception instanceof ModelAndViewDefiningException) {
    
    
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
    
    
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
    
    
        // 处理模型数据和渲染视图
        render(mv, request, response);
        if (errorView) {
    
    
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
    
    
        if (logger.isTraceEnabled()) {
    
    
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    
    
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
    
    
        // Exception (if any) is already handled..
        // 调用拦截器的afterCompletion()
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

12345678910111213141516171819202122232425262728293031323334353637383940414243

4. The execution process of SpringMVC

  1. The user sends a request to the server, and the request is captured by the SpringMVC front controller DispatcherServlet.
  2. DispatcherServlet parses the request URL, obtains the request resource identifier (URI), and determines the mapping corresponding to the request URI:

a) does not exist

i. Then judge whether mvc:default-servlet-handler is configured

ii. If not configured, the console will report that the mapping cannot be found, and the client will display a 404 error

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-Zdsbp3RM-1628192174722)(img/img006.png)]

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-0Bxr5kqK-1628192174723)(img/img007.png)]

iii. If there is a configuration, access the target resource (usually a static resource, such as: JS, CSS, HTML), and if the client cannot be found, a 404 error will be displayed

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-4P3E08qu-1628192174723)(img/img008.png)]

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-Jin8GZMJ-1628192174724)(img/img009.png)]

b) If it exists, execute the following process

  1. According to the URI, call HandlerMapping to obtain all related objects configured by the Handler (including the Handler object and the interceptor corresponding to the Handler object), and finally return it in the form of a HandlerExecutionChain execution chain object.
  2. DispatcherServlet selects an appropriate HandlerAdapter according to the obtained Handler.
  3. If the HandlerAdapter is successfully obtained, the interceptor's preHandler(...) method will be executed at this time [Forward]
  4. Extract the model data in the Request, fill in the Handler input parameters, start executing the Handler (Controller) method, and process the request. In the process of filling Handler's input parameters, according to your configuration, Spring will help you do some extra work:

a) HttpMessageConveter: convert the request message (such as Json, xml, etc.) into an object, and convert the object into the specified response information

b) Data conversion: perform data conversion on the request message. Such as converting String to Integer, Double, etc.

c) Data formatting: format the data of the request message. Such as converting a string to a formatted number or formatted date, etc.

d) Data verification: Verify the validity of the data (length, format, etc.), and store the verification results in BindingResult or Error

  1. After the Handler is executed, it returns a ModelAndView object to the DispatcherServlet.
  2. At this point, the interceptor's postHandle(...) method [reverse] will be executed.
  3. According to the returned ModelAndView (at this time, it will judge whether there is an exception: if there is an exception, execute HandlerExceptionResolver for exception handling) select a suitable ViewResolver for view resolution, and render the view according to the Model and View.
  4. After rendering the view, execute the afterCompletion(…) method of the interceptor [reverse].

= null);
}
}

おすすめ

転載: blog.csdn.net/weixin_44516623/article/details/128004464