Complete parsing of Spring MVC annotations

Learning is a step-by-step process. After we understand the background and concept of Spring MVC, we need to study Spring MVC in depth. We know that Spring MVC based on a set of MVC annotations, the ordinary POJO classes into controllers, without any interfaces, so annotations in Spring MVC status is not shaken, the following is an analysis of the annotations in Spring MVC.

@RequestMapping

First, let's look at Request Mapping, which is an important API that we used when writing introductory cases. It annotates other methods and specifies the request path.
Of course, RequestMapping is not just about modifying methods, it can also modify classes, such as the following example:

@RequestMapping("/springmvc")
@Controller
public class HelloWorld {

	@RequestMapping("/test")
	public String hello() {
		System.out.println("hello world");
		return "success";
	}
}

At this point, if you want to access the hello() method under this class through the url address, how should you write it (just add class annotations)? The url address is / spring MVC / test. Let's test it:

From this we know that:

  • Class Definition: Provides preliminary request mapping information relative to the Web application's root directory
  • Method Definition: Provides further mapping information, relative to the URL at the class definition, and if @RequestMapping is not marked at the class definition, then the marked URL at the method is relative to the root directory of the Web application.

Obviously, @RequestMapping does much more than that. Otherwise, I don't have to go to great lengths to write it. So let's look at its next function: mapping request parameters, request methods, or request headers.

1. Mapping request parameters, request methods, or request headers

@ In addition to mapping requests using request URL s, Request Mapping can also map requests using request methods, request parameters, and request headers.
@ The valie, method, params and heads of Request Mapping represent the request URL, request method, request parameter and request header respectively. The relationship between them is the same. Using multiple conditions together can make the request mapping more precise.
Look at an example:

@RequestMapping("/springmvc")
@Controller
public class HelloWorld {

	@RequestMapping(value = "/testMethod", method = RequestMethod.POST)
	public String testMethod() {
		System.out.println();
		return "success";
	}
}

Here we set the mapping path to / spring MVC / testMethod, which consists of class and method annotations, and then set the request mode to Post request.
Test page:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<form action="springmvc/testMethod" method="post">
		<input type="submit" value="submit">
	</form>
	
</body>
</html>

Operation results:

However, it should be noted that because you set the request mode as Post, if it is not Post requester, it will report an error and can not work properly.
This method is a very common configuration in practical development.

@ Request Mapping also supports the setting of request parameters and request headers. Request parameters and request headers support simple expressions, such as:

  • param1: Indicates that the request must contain a request parameter named param1
  • param1: Indicates that the request cannot contain a request parameter named param1
  • param!=value1: Represents that the request contains a request parameter named param1, but its value cannot be value1
  • {"param1=value1", "param2"}: indicates that the request must contain a request parameter named Param1 and param2, and that the value of the Param1 parameter must be value1

The request header is similar.

The mapping address set with @RequestMapping also supports wildcards, but wildcards must be Ant-style, and Ant-style resource addresses support three types of matchers:

  1. ?: Matches a character in a file name
  2. * Match any character in the file name
  3. **: Matching multilayer paths

Let's take an example:

@RequestMapping("/springmvc")
@Controller
public class HelloWorld {

	@RequestMapping("testAntPath/*/abc")
	public String testAntPath() {
		System.out.println("testAntPath");
		return "success";
	}
	
}

This configuration means that we can access this method by adding any character between testAntPath and abc, such as: / testAntPath/test/abc, / testAntPath/test2/abc, etc. The other two wildcards are similar, so no repetition will be made.

2.@PathVariable

@ PathVariable can map placeholders bound by URLs. A placeholder-bound URL is a new feature of Spring 3.0, which is a milestone in the development of Spring MVC towards REST goals.
By @PathVariable, placeholder parameters in the URL can be bound to the parameters of the controller processing method: the {xxx} placeholder in the URL can be bound to the parameters of the operation method through @PathVariable {"xxx"}.
Look at an example:

@RequestMapping("/springmvc")
@Controller
public class HelloWorld {

	@RequestMapping("testPathVariable/{id}")
	public String testPathVariable(@PathVariable("id") Integer id) {
		System.out.println("testPathVariable" + id);
		return "success";
	}

}

With this configuration, we can get the placeholder parameters in the URL address and bind them to the parameters of the processing method. Let's test:

Now that we're talking about REST, let's just talk about it.
REST: Representational State Transfer. Presentation layer state transition is the most popular Internet software architecture at present. It is clear in structure, standard-compliant, easy to understand and easy to expand, so it is being used by more and more websites.

  • Resources: An entity on the network, or a specific information on the network, can be a text, a picture, a song, a service, in short, a specific existence. A URI (Unified Resource Locator) can be used to point to it. Each resource corresponds to a specific URI. To obtain this resource, you can access its URI. Therefore, URI is a unique identifier for each resource.
  • Representation: The form in which resources are presented concretely is called its representational layer.
  • State Transfer: Each request represents an interaction between the client and the server. HTTP protocol is a stateless protocol, that is, all States are stored on the server side. Therefore, if the client wants to operate the server, it must make the server "state transition" by some means, and this transformation is based on the performance layer, so it is "performance layer transformation". Specifically, there are four action words in HTTP protocol: GET, POST, PUT and DELETE, which 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.

Examples:

  • / order/1 HTTP GET: Get order with id = 1
  • / order/1 HTTP DELETE: Delete order with id = 1
  • / order/1 HTTP PUT: Update order id = 1
  • / order HTTP POST: New order

The above is an example of a REST-style add-delete check.
But in fact, browser form only supports GET and POST requests, while DELETE, PUT and other requests are not supported. For this reason, Spring 3.0 adds a filter (Hidden http Method Filter), which can transform these requests into standard http methods, making browsers indirectly support GET, POST, PUT and DELETE requests.

A lot has been said. It's not as clear as a real case. Let's use the filter through an example.
First, configure the filter in the web.xml file:

<!-- To configure org.springframework.web.filter.HiddenHttpMethodFilter:You can post Request changed to delete or put request -->
	<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>

Then we create four forms on the jsp page, which correspond to four requests:

	<form action="springmvc/testRest/1" method="post">
		<input type="hidden" name="_method" value="PUT"> 
		<input type="submit" value="TestRest PUT">
	</form>
	<br>
	
	<form action="springmvc/testRest/1" method="post">
		<input type="hidden" name="_method" value="DELETE"> 
		<input type="submit" value="TestRest DELETE">
	</form>
	<br>
	
	<form action="springmvc/testRest" method="post">
		<input type="submit" value="TestRest POST">
	</form>
	<br>
	
	<form action="springmvc/testRest/1" method="get">
		<input type="submit" value="TestRest GET">
	</form>
	<br>
	
</body>
</html>

Then four test methods are written in the controller:

@RequestMapping("/springmvc")
@Controller
public class HelloWorld {
	
	@RequestMapping(value = "/testRest/{id}", method = RequestMethod.PUT)
	public String testRestPut(@PathVariable Integer id) {
		System.out.println("testRest PUT: " + id);
		return "success";
	}
	
	@RequestMapping(value = "/testRest/{id}", method = RequestMethod.DELETE)
	public String testRestDelete(@PathVariable Integer id) {
		System.out.println("testRest DELETE: " + id);
		return "success";
	}
	
	@RequestMapping(value = "/testRest", method = RequestMethod.POST)
	public String testRestPost() {
		System.out.println("testRest POST");
		return "success";
	}

	@RequestMapping(value = "/testRest/{id}", method = RequestMethod.GET)
	public String testRestGet(@PathVariable Integer id) {
		System.out.println("testRest GET: " + id);
		return "success";
	}
}

As mentioned earlier, if the client's actual request mode does not match the request mode set by @RequestMapping, the program will report an error, so if all four request modes can be accessed successfully, it shows that the request mode has been successfully converted by Hidden HttpMethodFilter.
Next run to see the results:

There is no problem with the results of the operation, but there are some things to be noted here. I have also encountered many pits here. Let me share with you:

  1. PUT and DELETE requests are unrecognizable, so to be assisted by a hidden domain, the name of the hidden domain must be "_method", and the value I must be the name of the request mode, which is determined by the bottom of the filter.
  2. If you encounter the following problems, then you have three solutions

    a.tomcat to 7.0 and below
    b. The request is forwarded to a Controller and returned to the jsp page
    C. <%@ page language = "java" contentType = "text/html; charset=UTF-8" page Encoding = "UTF-8" isErrorPage = "true"%>, set isErrorPage to true

For the time being, I have only met these two problems.

@RequestParam

Previously, we used @PathVariable to get the request parameters. This is a Rest-style URL, which carries a parameter by exploding placeholders, but it is not really a request parameter. We should use @RequestParam to process the request parameters.

Binding request parameters with @RequestParam

Using @RequestParam in the parameters of the processing method can pass the request parameters to the request method

  • value: parameter name
  • required: Is it necessary, default to true, to indicate that the request parameter must contain the corresponding parameters, if it does not exist, an exception will be thrown

Look at one case:

@RequestMapping("/springmvc")
@Controller
public class HelloWorld {

	/**
	 * @RequestParam 	Used to map request parameters
	 * value			Value is the parameter name of the request
	 * required 		Whether the request parameter must, by default, be true, must
	 * defaultValue		Default values of request parameters
	 * @param username
	 * @param age
	 * @return
	 */
	@RequestMapping(value = "/testRequestParam")
	public String testRequestParam(@RequestParam(value = "username") String username,
			@RequestParam(value = "age") int age) {
		System.out.println("testRequestParam,username = " + username + "--- age = " + age);
		return "success";
	}
}

At this point we access a URL like this:

http://localhost:8080/SpringMVC/springmvc/testRequestParam?username=abc&age=20

The results are as follows:

testRequestParam,username = abc--- age = 20

As mentioned earlier, required defaults to true, so parameters configured through @RequestParam must be carried when sending requests, otherwise an exception will be thrown. If you want to set a parameter as non-essential, you can access it successfully without carrying it, just set the required attribute of the parameter to false.

	@RequestMapping(value = "/testRequestParam")
	public String testRequestParam(@RequestParam(value = "username") String username,
			@RequestParam(value = "age", required = false) int age) {
		System.out.println("testRequestParam,username = " + username + "--- age = " + age);
		return "success";
	}

@RequestHeader

@ RequestHeader and @RequestParam are used the same way. It can get the information of the request header and inject it into the parameters of the target method. Let's look at the case:

@RequestMapping("/springmvc")
@Controller
public class HelloWorld {

	@RequestMapping(value = "/testRequestHeader")
	public String testRequestHeader(@RequestHeader(value="Accept-Language") String al) {
		System.out.println("testRequestHeader,Accept-Language: " + al);
		return "success";
	}
}

Visit http://localhost:8080/Spring MVC/springmvc/testRequest Header for testing. The results are as follows:

testRequestHeader,Accept-Language: zh-CN

@CookieValue

@ The CookieValue annotation, as you can see from its name, is used to get cookie values. It can get cookie values and inject them into the parameters of the target method. Look at an example:

@RequestMapping("/springmvc")
@Controller
public class HelloWorld {
	
	@RequestMapping(value = "/testCookieValue")
	public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
		System.out.println("testCookieValue,sessionId: " + sessionId);
		return "success";
	}
}

Visit http://localhost:8080/Spring MVC/springmvc/testCookieValue for testing. The results are as follows:

testCookieValue,sessionId: B7EB860C3764C4BAB0C1F526B830C1FF

Binding request parameter values with POJO objects

We know that a form contains a lot of information and usually encapsulates these data as an object. At this time, if every parameter is mapped with @RequestParam, the cost is too high, then we can directly use an object as a request parameter. Spring MVC automatically matches the request parameter name and POJO attribute name, fills the attribute value for the object automatically. It also supports cascading attributes, such as dept.deptId, dept.adress.tel. Let's take an example.
First write the form:

<form action="springmvc/testPojo" method="post">
		username:<input type="text" name="username">
		<br>
		password:<input type="password" name="password">
		<br>
		email:<input type="text" name="email">
		<br>
		age:<input type="text" name="age">
		<br>
		city:<input type="text" name="address.city">
		<br>
		province:<input type="text" name="address.province">
		<br>
		<input type="submit" value="submit">
	</form>

Then create two Bean classes:

public class User {

	private String username;
	private String password;
	private String email;
	private int age;

	private Address address;

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "User [username=" + username + ", password=" + password + ", email=" + email + ", age=" + age
				+ ", address=" + address + "]";
	}
}
public class Address {
	
	private String province;
	private String city;
	public String getProvince() {
		return province;
	}
	public void setProvince(String province) {
		this.province = province;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	
	@Override
	public String toString() {
		return "Address [province=" + province + ", city=" + city + "]";
	}
}

Because it supports cascading attributes, there is no problem with address.city and address.province in the form.
Test methods:

@RequestMapping("/springmvc")
@Controller
public class HelloWorld {
	
	@RequestMapping("/testPojo")
	public String testPojo(User user) {
		System.out.println("testPojo: " + user);
		return "success";
	}
}

Visit http://localhost:8080/Spring MVC/springmvc/testPojo for testing. The results are as follows:

testPojo: User [username=admin, password=123456, email=admin@163.com, age=20, address=Address [province=ZheJiang, city=HangZhou]]

This method of injecting form parameters is still used a lot, so we must master it.

Recommended reading

Complete Analysis of Spring MVC Annotations (Part 2)

Tags: Spring REST Attribute Java

Posted on Mon, 26 Aug 2019 22:31:50 -0700 by Meissa