Detailed tutorial of spring MVC framework

Keywords: Java JSP Spring JSON xml

Although this section is simple, it's very tedious. You can first understand the use of @ RequestMapping. It doesn't matter if you don't understand it very well. First, continue to learn. When you look back again, you will find that @ RequestMapping is so simple!

@RequestMapping

@RequestMapping can be used on controller classes or controller methods.

Annotations at the class level map a specific request or request pattern to a controller. You can then add additional method level annotations to further specify the mapping relationship to the processing method.

Basic usage:

The following @ RequestMapping("/index") is equivalent to @ RequestMapping(value = "/index")

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/index")
public class HelloWorldController {

	@RequestMapping(value = "/hello", method = RequestMethod.GET)
	public String hello() {

		return "/WEB-INF/views/success.jsp";
	}

	@RequestMapping(value = "/world", method = RequestMethod.POST)
	public String world() {

		return "/WEB-INF/views/success.jsp";
	}

}

Method parameter support: GET, PUT, POST, DELETE and PATCH. Use method to limit the types of requests you accept.

The hello() method will only accept requests with the request method of GET and the request address of / index/hello.

The world() method will only accept requests with the request mode of POST and the request address of / index/world.

Map multiple addresses:

@RequestMapping can also map multiple requests to a method, just give value a list containing multiple paths.

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/index")
public class HelloWorldController {

	@RequestMapping(value = {"/hello", "/world", "/helloworld"})
	public String hello() {

		return "/WEB-INF/views/success.jsp";
	}

}

URI template:

The URI template can provide great convenience for quick access to a specific part of the URL specified in @ RequestMapping.

Use @ PathVariable to get the value of {name} and output it in the console. For example, the request address is: http://localhost:8080/SpringMVC/hello/jack Then the console will output jack.

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloWorldController {

	@RequestMapping("/hello/{name}")
	public String hello(@PathVariable String name) {
		System.out.println(name);

		return "/WEB-INF/views/success.jsp";
	}

}

If the URI variable in the path is not the same as the parameter name in the method, you need the binding parameter displayed in @ PathVariable.

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloWorldController {

	@RequestMapping("/hello/{name}")
	public String hello(@PathVariable("name") String username) {
		System.out.println(username);

		return "/WEB-INF/views/success.jsp";
	}

}

A method can have any number of @PathVariable annotations:

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloWorldController {

	@RequestMapping("/hello/{name}/age/{age}")
	public String hello(@PathVariable String name, @PathVariable int age) {
		System.out.println("name:" + name + ",age:" + age);

		return "/WEB-INF/views/success.jsp";
	}

}

@PathVariable can be applied to parameters of all simple types, such as int, long, Date, etc. Spring will automatically help you convert parameters to the appropriate type. If the conversion fails, a typemismatch exception will be thrown. You can also register your own classes if you need to handle transformations of other data types.

URI template with regular expression:

You can use regular expressions to accurately describe acceptable request paths:

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloWorldController {

	@RequestMapping("/hello/{name:[a-z]+}/age/{age}")
	public String hello(@PathVariable String name, @PathVariable int age) {
		System.out.println("name:" + name + ",age:" + age);

		return "/WEB-INF/views/success.jsp";
	}

}

Ant style path mode:

In addition to the URI template, the @ RequestMapping annotation also supports Ant style path patterns (such as / hello/*.do, etc.). In addition, URI template variables can be combined with Ant style glob s (such as / hello/*/user/{userId}). Where * represents any string. But if / is encountered, it will be considered as the URI of the next part, so there cannot be / in *.

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloWorldController {

    // Matching address: http://localhost:8080/SpringMVC/hello/jack/user/18
	@RequestMapping("/hello/*/user/{userId}")
	public String hello(@PathVariable String userId) {
		System.out.println(userId);

		return "/WEB-INF/views/success.jsp";
	}

}

So what should I do if I want to match the path with a / band? You can use * * to match. For example, / hello/**/user/{userId} will match the part between / hello / and / user/{userId}.

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloWorldController {

    // Matching address: http://localhost:8080/SpringMVC/hello/jack/tom/cat/user/18
	@RequestMapping("/hello/**/user/{userId}")
	public String hello(@PathVariable String userId) {
		System.out.println(userId);

		return "/WEB-INF/views/success.jsp";
	}

}

Path pattern comparison:

When a URL matches multiple pattern s at the same time, we need an algorithm to determine the best one.

The path template with the least sum of URI template variables and wildcards is more accurate. For example, / hotels / {hotels} / * has a URI variable and a wildcard, while / hotels / {hotels} / * * has a URI variable and two wildcards, so we think the former is a more accurate path template.

If the sum of the number of URI templates and wildcards of the two templates is the same, the template with a longer path is more accurate. For example, / foo/bar * is considered more accurate than / foo / * because the former has a longer path.

If the number and length of the two templates are the same, the template with fewer wildcards is more accurate. For example, / hotels / {hotels} is more accurate than / hotels / *.

In addition, there are other rules:

  • The default configuration mode / * * is more "inaccurate" than all other modes. For example, / api/{a}/{b}/{c} is more accurate than the default configuration mode / *
  • Prefix wildcards (such as / public / * *) are considered less accurate than any other pattern that does not include double wildcards. For example, / public/path3/{a}/{b}/{c} is more accurate than / public / *

Refer to these two classes for more details: AntPatternComparator and AntPathMatcher. It is worth mentioning that the PathMatcher class is configurable.

Suffix pattern matching:

Spring MVC uses the suffix pattern matching of "*" by default for path matching, so a controller mapped to / person path will also be implicitly mapped to / person. *. This makes it easier to request different formats of the same resource file through a URL (for example, / person.pdf, / person.xml).

Turn off suffix pattern matching:

java:

@Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {

        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            configurer
                // Turn off suffix pattern matching
                .setUseSuffixPatternMatch(false)
                .setUseTrailingSlashMatch(false)
                .setUseRegisteredSuffixPatternMatch(true)
                .setPathMatcher(antPathMatcher())
                .setUrlPathHelper(urlPathHelper());
        }

        @Bean
        public UrlPathHelper urlPathHelper() {
            //...
        }

        @Bean
        public PathMatcher antPathMatcher() {
            //...
        }

    }

xml:

 <mvc:annotation-driven>
        <mvc:path-matching
			<!-- Turn off suffix pattern matching -->                           
            suffix-pattern="false"
            trailing-slash="false"
            registered-suffixes-only="true"
            path-helper="pathHelper"
            path-matcher="pathMatcher"/>
    </mvc:annotation-driven>

    <bean id="pathHelper" class="org.example.app.MyPathHelper"/>
    <bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>

Matrix variable:

Matrix variables can appear in any path segment, and each pair of matrix variables is separated by a semicolon ";". For example, such a URI: "/ cars;color=red;year=2012". Multiple values can be separated by commas "color=red,green,blue," or repeated multiple times with variable names "color=red;color=green;color=blue.".

If a URL may need to contain matrix variables, the URI template is needed to reflect this in the mapping configuration of the request path. This ensures that the request can be mapped correctly, regardless of whether the matrix variable appears in the URI, in what order, and so on. Use @ MatrixVariable in the method parameter to get the value in the matrix variable.

Before using matrix variables, you need to turn on auto parsing matrix variables in the springmvc configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven enable-matrix-variables="true"/>

</beans>

The following code uses matrix variables:

After using the / pets/42;q=11;r=22 path to request, the console will output petId:42,q:11.

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MatrixController {

	@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET)
	public void findPet(@PathVariable String petId, @MatrixVariable(name = "q", pathVar = "petId") int q) {
		System.out.println("petId:" + petId + "," + "q:" + q);
	}

}

Since any path segment can contain matrix variables, in some scenarios, you need to specify the location of a matrix variable with more accurate information:

After using the / owners/42;q=11/pets/21;q=22 path to request, the console will output q1:11,q2:22.

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MatrixController {

	@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
	public void findPet(@MatrixVariable(name = "q", pathVar = "ownerId") int q1,
			@MatrixVariable(name = "q", pathVar = "petId") int q2) {
		System.out.println("q1:" + q1 + "," + "q2:" + q2);
	}

}

You can also declare that a matrix variable is not required and assign it a default value:

After using the / pets/42 path to request, the console will output q:1.

package com.pudding.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MatrixController {

	@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET)
	public void findPet(@MatrixVariable(required = false, defaultValue = "1") int q) {
		System.out.println("q:" + q);
	}

}

It can be observed that there is no q parameter in the requested path. After the required = false and DefaultValue = 1 is configured, if there is no specified matrix variable in the path, spring MVC will automatically set the default value for the matrix variable.

You can also store all matrix variables through a Map:

After using the / owners/42;q=11;r=12/pets/21;q=22;s=23 path to request, the console will output matrixVars:{q=11, r=12, s=23} and petMatrixVars:{q=22, s=23}.

package com.pudding.controller;

import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MatrixController {

	@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
	public void findPet(@MatrixVariable Map<String, String> matrixVars,
			@MatrixVariable(pathVar = "petId") Map<String, String> petMatrixVars) {
		System.out.println("matrixVars:" + matrixVars);
		System.out.println("petMatrixVars:" + petMatrixVars);
	}

}

consumes:

You can narrow the mapping by specifying a set of consumable media types. In this way, the request will be matched only when the content type value in the request header is the same as that in the specified consumable media type. For example:

@Controller
@RequestMapping(path = "/pets", method = RequestMethod.POST, consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {
    // Method implementation omission
}

Negation can also be used in the expression that specifies the consumable media type. For example, you can use! text/plain to match all requests that do not contain text/plain in the content type of the request header. At the same time, some constants are defined in the MediaType class, such as application JSON value, application JSON utf8 value and so on. It is recommended to use them more.

produces:

You can specify a set of producible media types to narrow the mapping. In this way, the request will only be matched if the Accept value in the request header is the same as that in the specified producible media type. Furthermore, using the produces condition ensures that the content used to generate the response is the same as the specified producible media type. for instance:

@RestController
@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Pet getPet(@PathVariable String petId, Model model) {
    // Method implementation omission
}

Similar to the consumers condition, a producible media type expression can also use negation. For example, you can use! text/plain to match all requests without text/plain in the request header Accept. At the same time, some constants are defined in the MediaType class, such as application JSON value, application JSON utf8 value and so on. It is recommended to use them more.

Request parameters and request header values:

You can filter the conditions of the request parameters to narrow the request matching range, such as "myParam", "myParam",! myParam "and" myParam=myValue ". The first two conditions are used to filter requests with or without certain request parameters, and the third condition is used to filter requests with specific parameter values. Here is an example of how to use the filter criteria for request parameter values:

@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {

    @RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue")
    public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
        // Actual implementation omission
    }

}

Similarly, you can use the same criteria to filter whether the request header appears or not, or filter out a request header with a specific value:

@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {

    @RequestMapping(path = "/pets", method = RequestMethod.GET, headers="myHeader=myValue")
    public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
        // Method implementation omission
    }

}

Note: part of the above information is from W3C school translation Spring MVC official document

Posted by vbnullchar on Fri, 17 Apr 2020 08:51:37 -0700