Mind mapping
Wechat public account has been opened: [Java technology lovers], remember to pay attention to those who haven’t
This article has been included in my Github selection, welcome to Star: github.com/yehongzhi/l…
An overview of the
SpringMVC is a very familiar framework, because the most popular built-in MVC framework for SpringBoot is SpringMVC. My motivation for writing this article is to review and relearn SpringMVC.
To understand SpringMVC, look at a process diagram:
From the flow chart, we can see:
- Receiving Request requests from the front-end.
- Find the corresponding processor to process the request according to the mapping path, and return to ModelAndView after processing.
- Perform view parsing, view rendering, and return response results.
The summary is: parameters receive, define mapping path, page jump, return response result.
Of course, this is just the most basic core functionality, in addition to the definition of interceptors, global exception handling, file upload and download, etc.
I. Construction project
In the old project, since there was no SpringBoot and no automatic configuration, you needed to use the web. XML file to define a DispatcherServlet. Most Internet applications now use SpringBoot, so I’m going to go straight to SpringBoot. It’s easy to introduce dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Copy the code
2. Define Controller
There are five ways to define the Controller processor using SpringMVC.
2.1 Implement the Controller interface
Early SpringMVC was defined this way:
/ * * *@authorYe Hongzhi public account: Java technology enthusiast *@name DemoController
* @dateThe 2020-08-25 "* * /
@org.springframework.stereotype.Controller("/demo/controller")
public class DemoController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// Business processing
return null; }}Copy the code
2.2 Implement the HttpRequestHandler interface
Similar to the first approach, this is also done by implementing the interface:
/ * * *@authorYe Hongzhi public account: Java technology enthusiast *@name HttpDemoController
* @dateThe 2020-08-25 if * * /
@Controller("/http/controller")
public class HttpDemoController implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
// Business processing}}Copy the code
2.3 Implementing the Servlet Interface
This approach is no longer recommended, but you can see that the underlying SpringMVC uses servlets.
@Controller("/servlet/controller")
public class ServletDemoController implements Servlet {
// The following are the Servlet lifecycle methods
@Override
public void init(ServletConfig servletConfig) throws ServletException {}@Override
public ServletConfig getServletConfig(a) {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {}@Override
public String getServletInfo(a) {
return null;
}
@Override
public void destroy(a) {}}Copy the code
Since this is not recommended, the adapter is not loaded by default. Add:
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public SimpleServletHandlerAdapter simpleServletHandlerAdapter(a) {
return newSimpleServletHandlerAdapter(); }}Copy the code
2.4 using the @ RequestMapping
This approach is the most common because the above definitions require a single class to define a path, resulting in many classes. Using annotations is relatively lightweight.
@Controller
@RequestMapping("/requestMapping/controller")
public class RequestMappingController {
@RequestMapping("/demo")
public String demo(a) {
return "HelloWord"; }}Copy the code
2.4.1 Restful style supported
It also supports Restful style, using the method attribute to define the operation mode on the resource:
@RequestMapping(value = "/restful", method = RequestMethod.GET)
public String get(a) {
/ / query
return "get";
}
@RequestMapping(value = "/restful", method = RequestMethod.POST)
public String post(a) {
/ / create
return "post";
}
@RequestMapping(value = "/restful", method = RequestMethod.PUT)
public String put(a) {
/ / update
return "put";
}
@RequestMapping(value = "/restful", method = RequestMethod.DELETE)
public String del(a) {
/ / delete
return "post";
}
Copy the code
2.4.2 Ant style is supported
// Match /antA or /antB urls
@RequestMapping("/ant?" )
public String ant(a) {
return "ant";
}
// Match /ant/a/create or /ant/b/create urls
@RequestMapping("/ant/*/create")
public String antCreate(a) {
return "antCreate";
}
// Match /ant/create or /ant/a/b/create urls
@RequestMapping("/ant/**/create")
public String antAllCreate(a) {
return "antAllCreate";
}
Copy the code
2.5 use HandlerFunction
The last is to use the HandlerFunction functional interface, which was introduced after Spring5.0 and is mainly used for reactive interface development, namely Webflux development.
Those who are interested can search relevant data on the Internet to learn. This may be very large in length and is not repeated here.
3. Receiving parameters
After you define the Controller, you need to receive the parameters from the front end. How do you receive them?
3.1 Receiving Common Parameters
RequestMapping = RequestMapping = RequestMapping = RequestMapping = RequestMapping
@RequestMapping(value = "/restful", method = RequestMethod.POST)
public String post(Integer id, String name, int money) {
System.out.println("id:" + id + ",name:" + name + ",money:" + money);
return "post";
}
Copy the code
3.2 @requestParam Parameter name binding
If you don’t want to use the parameter name as the parameter name, you can use @requestParam to bind the parameter name:
/** * value: parameter name * required: Indicates whether this parameter must be included in the request. The default value is true. * defaultValue: indicates the defaultValue */
@RequestMapping(value = "/restful", method = RequestMethod.GET)
public String get(@RequestParam(value = "userId", required = false, defaultValue = "0") String id) {
System.out.println("id:" + id);
return "get";
}
Copy the code
3.3 @pathvariable Path parameter
Map the placeholder {XXX} parameter in the URL to the input parameter of the action method via @pathvariable. The demo code is as follows:
@RequestMapping(value = "/restful/{id}", method = RequestMethod.GET)
public String search(@PathVariable("id") String id) {
System.out.println("id:" + id);
return "search";
}
Copy the code
3.4 @requestheader bind the RequestHeader attribute
How to get the information about the request header?
Use the @requestheader annotation in a similar way to @requestParam:
@RequestMapping("/head")
public String head(@RequestHeader("Accept-Language") String acceptLanguage) {
return acceptLanguage;
}
Copy the code
3.5 @cookievalue Binds the Cookie value of the request
Get the value of Cookie in Request:
@RequestMapping("/cookie")
public String cookie(@CookieValue("_ga") String _ga) {
return _ga;
}
Copy the code
3.6 Binding request parameters to POJO objects
Defines a User entity class:
public class User {
private String id;
private String name;
private Integer age;
// Getter and setter methods
}
Copy the code
Define a @requestMapping operation:
@RequestMapping("/body")
public String body(User user) {
return user.toString();
}
Copy the code
As long as the request parameter is the same as the attribute name, the user object is automatically populated:
3.6.1 Cascading properties are supported
Now we have an Address class that stores Address information:
public class Address {
private String id;
private String name;
// Getter and setter methods
}
Copy the code
Add the address attribute to User:
public class User {
private String id;
private String name;
private Integer age;
private Address address;
// Getter and setter methods
}
Copy the code
Address. Name and address. Id will be filled automatically if they are passed as parameters:
3.6.2 @initBinder resolves property name conflicts when receiving multiple objects
If you have two POJO objects with the same property name, isn’t that a conflict? For example, user and address have id and name attributes, which would conflict if received at the same time:
// Both user and address have id and name attributes
@RequestMapping(value = "/twoBody", method = RequestMethod.POST)
public String twoBody(User user, Address address) {
return user.toString() + "," + address.toString();
}
Copy the code
You can use @initbinder to bind the parameter name:
@InitBinder("user")
public void initBindUser(WebDataBinder webDataBinder) {
webDataBinder.setFieldDefaultPrefix("u.");
}
@InitBinder("address")
public void initBindAddress(WebDataBinder webDataBinder) {
webDataBinder.setFieldDefaultPrefix("addr.");
}
Copy the code
3.6.3 @RequestBody automatically parses JSON strings to encapsulate objects
The front-end passes in a JSON string, which is automatically converted to a POJO object.
@RequestMapping(value = "/requestBody", method = RequestMethod.POST)
public String requestBody(@RequestBody User user) {
return user.toString();
}
Copy the code
Note that to use a POST request, the content-type on the sender side is set to Application /json and the data is a JSON string:
There are even some people who prefer to receive with a Map:
However, do not use a Map to receive data, otherwise the code will be difficult to maintain, the old man may not understand your Map in the data, so it is best to define a POJO object.
4. Parameter type conversion
In fact, the SpringMVC framework has a number of type converters built in, such as the number you pass in as a string and the input parameter you receive as int, long, will automatically convert it for you.
In package of org. Springframework. Core. The convert. The converter, as shown in figure:
Sometimes if the built-in type converter is not enough to meet the business needs, how to expand, very simple, see me operation. What is a Java technology enthusiast (tactical backward)?
First of all, the built-in Converter implements the Converter interface, which I also implement:
public class StringToDateConverter implements Converter<String.Date> {
@Override
public Date convert(String source) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
//String is converted to Date
return sdf.parse(source);
} catch (Exception e) {
// Type conversion error
e.printStackTrace();
}
return null; }}Copy the code
Then register the converter with the Spring container:
@Configuration
public class ConverterConfig extends WebMvcConfigurationSupport {
@Override
protected void addFormatters(FormatterRegistry registry) {
// Add type converter
registry.addConverter(newStringToDateConverter()); }}Copy the code
All Date strings are automatically converted to Date, which is very convenient:
5. Page hopping
Before the separation of the front and back end, the work of the page jump is controlled by the back end, using JSP to display data. Although JSP is almost never used in Internet projects today, I felt I needed to learn because some older projects still use JSP or require refactoring.
If you return a string in the RequestMapping method, you will not be redirected to the specified JSP page, which requires some configuration.
The first step is to add the Maven configuration to parse the JSP.
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>7.0.59</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
Copy the code
Second, add a view parser.
@Configuration
public class WebAppConfig extends WebMvcConfigurerAdapter {
@Bean
public InternalResourceViewResolver viewResolver(a) {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
returnviewResolver; }}Copy the code
Step 3: Set the IDEA configuration.
Step 4, create the JSP page.
Step 5: Create Controller Controller.
@Controller
@RequestMapping("/view")
public class ViewController {
@RequestMapping("/hello")
public String hello(a) throws Exception {
return "hello"; }}Copy the code
That’s it, start the project, visit /view/hello and see:
It’s that simple, right
Vi. @ ResponseBody
If the front and back end separation is adopted, the page jump does not need the back end control, the back end only needs to return JSON, how to return?
Just use the @responseBody annotation, which automatically returns the object as JSON data.
The @responseBody annotation can be placed on a class or method as follows:
// for classes and methods
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}
Copy the code
To demonstrate:
@RequestMapping("/userList")
@ResponseBody
public List<User> userList(a) throws Exception {
List<User> list = new ArrayList<>();
list.add(new User("1".Yao Daqiu.18));
list.add(new User("2"."Li Xing Xing".18));
list.add(new User("3"."Dong min".18));
return list;
}
Copy the code
Test /view/userList:
Seven, @ ModelAttribute
There are many uses of @modelattribute, which are explained in the following sections.
7.1 For methods with no return value
In the Controller class, the @ModelAttribute annotation method is executed before all RequestMapping methods.
@Controller
@RequestMapping("/modelAttribute")
public class ModelAttributeController {
// Execute this method first
@ModelAttribute
public void modelAttribute(Model model){
// Put data in the request field
model.addAttribute("userName"."Public account: Java Technology enthusiast");
}
@RequestMapping("/index")
public String index(a){
// Jump to the inex.jsp page
return "index"; }}Copy the code
The index.jsp page looks like this:
<%@ page contentType="text/html; charset=UTF-8" language="java"% > < HTML > < head > < title > home page < / title > < / head > < body > <! - get the attribute value - > < h1 > ${userName} < / h1 > < / body > < / HTML >Copy the code
Like a Controller interceptor, the @ModelAttribute annotation method is executed before RequestMapping. So be careful with it.
Start the project and access /modelAttribute/index to see:
Even if the userName attribute value is not put in the index() method, the JSP page will get it because the modelAttribute() method was put in before the index() method was executed.
7.2 On a method that returns a value
RequestMapping (RequestMapping, RequestMapping, RequestMapping, RequestMapping, RequestMapping, RequestMapping) ¶
// put it on the method with arguments
@ModelAttribute
public User userAttribute(a) {
// equivalent to model.addattribute ("user",new user ("1", "Java technology enthusiast ", 18));
return new User("1"."Java Technology enthusiast".18);
}
@RequestMapping("/user")
public String user(a) {
return "user";
}
Copy the code
Create a user.jsp:
<%@ page contentType="text/html; charset=UTF-8" language="java"% > < HTML > < head > < title > home page < / title > < / head > < body > < h1 > ID: ${user. ID} < / h1 > < h1 > name: ${user. The name} < / h1 > <h1> age :${user.age} age </h1> </body> </ HTML >Copy the code
Test it out:
The default value of the property that you put into the Request field is the first letter of the class name lowercase camel case, if you want to customize it? It’s easy to write:
// Custom attribute named "u"
@ModelAttribute("u")
public User userAttribute(a) {
return new User("1"."Java Technology enthusiast".18);
}
/ * * the JSP will change to write like this: < h1 > ID: ${u.i d} < / h1 > < h1 > name: ${u.n ame} < / h1 > < h1 > age: ${u.a ge}, < / h1 > * /
Copy the code
7.3 Putting it in RequestMapping
@Controller
@RequestMapping("/modelAttribute")
public class ModelAttributeController {
@RequestMapping("/jojo")
@ModelAttribute("attributeName")
public String jojo(a) {
return "Jo-jo! I don't want to be a human being!"; }}Copy the code
In this case, the value returned by the RequestMapping method is not the JSP view. Instead, you put the return value into the value of an attribute in the Request field called attributeName. The view is the URL in the RequestMapping annotation, so create a corresponding JSP page:
<%@ page contentType="text/html; charset=UTF-8" language="java"% > < HTML > < head > < title > home page < / title > < / head > < body > < h1 > ${attributeName} < / h1 > < / body > < / HTML >Copy the code
Test it out:
7.4 Put it on the method input parameter
Put on an input parameter, which means to extract the corresponding attribute value from the previous Model and pass it into the method as an input parameter. As follows:
@ModelAttribute("u")
public User userAttribute(a) {
return new User("1"."Java Technology enthusiast".18);
}
@RequestMapping("/java")
public String user1(@ModelAttribute("u") User user) {
// Take the value returned by the @modelAttribute ("u") method and print it
System.out.println("user:" + user);
return "java";
}
Copy the code
Test it out:
8. Interceptor
Interceptors are important. They are used for many purposes, such as login verification, permission verification, and so on. How does SpringMVC add interceptors?
The HandlerInterceptor interface has three methods that need to be overwritten.
- PreHandle () : Called before the request is processed by the business handler. Pretreatment.
- PostHandle () : executed after the business processor completes the execution of the request and before the view is generated. Post-processing.
- AfterCompletion () : Called after the DispatcherServlet has fully processed the request and can be used to clean up resources, etc. Return processing (the page has already been rendered);
Custom interceptor, implementation of the interface HandlerInterceptor:
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Preprocess, return true and continue execution. If login verification is required, return false if login verification fails or true if login verification passes.
System.out.println("Execute the preHandle() method");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
/ / post-processing
System.out.println("Execute postHandle() method");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// called after the DispatcherServlet has fully processed the request
System.out.println("Execute afterCompletion() method"); }}Copy the code
Then add the interceptor to the Spring container:
@Configuration
public class ConverterConfig extends WebMvcConfigurationSupport {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/ * *"); }}Copy the code
/** represents all paths.
Global exception handling
SpringMVC itself handles some exceptions globally, so there’s a built-in exception handler, where is that?
See the class diagram for the HandlerExceptionResolver interface:
You can see from the class diagram that there are four types of exception handlers:
DefaultHandlerExceptionResolver
, default exception handler. Returns different exception views based on different types of exceptions.SimpleMappingExceptionResolver
, simple mapping exception handlers. Resolve exceptions by configuring the relationship between the exception class and the View.ResponseStatusExceptionResolver
, status code exception handler. Resolve with@ResponseStatus
Annotation type exception.ExceptionHandlerExceptionResolver
Exception handlers in the form of annotations. right@ExceptionHandler
Annotation method for exception resolution.
The first default exception handler is the built-in exception handler, which is generally ignored for some common exception handling. The last three are the ones to watch out for, and are for extension.
9.1 SimpleMappingExceptionResolver
This translates to simply mapping exception handlers. The purpose is that we can specify an exception and jump to the specified page when it is thrown. Look at the demonstration.
Add the spring-config. XML file to the resources directory.
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<! Define the default exception handling page -->
<property name="defaultErrorView" value="err"/>
<! -- Define the name of the attribute used by the exception handling page to obtain the exception information. Default is exception -->
<property name="exceptionAttribute" value="ex"/>
<! -- Define exceptions that require special handling, using the class name or full pathname as the key and the exception as the page name as the value -->
<property name="exceptionMappings">
<props>
<! -- exception, err means err.jsp page -->
<prop key="java.lang.Exception">err</prop>
<! -- Can be configured with multiple prop -->
</props>
</property>
</bean>
</beans>
Copy the code
The second step is to load the XML file in the startup class:
@SpringBootApplication
@ImportResource("classpath:spring-config.xml")
public class SpringmvcApplication {
public static void main(String[] args) { SpringApplication.run(SpringmvcApplication.class, args); }}Copy the code
Step 3: Create an err.jsp page in the webApp directory:
<%@ page contentType="text/html; charset=UTF-8" language="java"%> < HTML > <head> <title> </head> <body> <h1500Page </h1> <br> <%-- Print Exception to page --%> <% Exception ex = (Exception)request.getAttribute("ex"); %>
<br>
<div><%=ex.getMessage()%></div>
<% ex.printStackTrace(new java.io.PrintWriter(out)); %>
</body>
</html>
Copy the code
That’s it. Write an interface test:
@Controller
@RequestMapping("/exception")
public class ExceptionController {
@RequestMapping("/index")
public String index(String msg) throws Exception {
if ("null".equals(msg)) {
// Throw a null pointer exception
throw new NullPointerException();
}
return "index"; }}Copy the code
The effect is as follows:
This kind of exception handler is rarely seen in today’s projects where the front and back ends are separated.
9.2 ResponseStatusExceptionResolver
This exception handler is primarily used to handle exceptions with the @responseStatus annotation. Look at the demo code:
Define an exception class and decorate it with the @responseStatus annotation:
// The HttpStatus enumeration has all the status codes and returns a response code of 400
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class DefinedException extends Exception{}Copy the code
Write a Controller interface to test:
@RequestMapping("/defined")
public String defined(String msg) throws Exception {
if ("defined".equals(msg)) {
throw new DefinedException();
}
return "index";
}
Copy the code
Start the project and test it. It looks like this:
9.3 ExceptionHandlerExceptionResolver
Exception handlers in the form of annotations, which are the most commonly used. It is very simple and convenient to use.
Step 1, define a custom BaseException:
public class BaseException extends Exception {
public BaseException(String message) {
super(message); }}Copy the code
Second, define an error notification entity class ErrorInfo:
public class ErrorInfo {
public static final Integer OK = 0;
public static final Integer ERROR = -1;
private Integer code;
private String message;
private String url;
/ / getter and setter
}
Copy the code
Third, define the GlobalExceptionHandler class:
// RestControllerAdvice is used here, which is a combination of @responseBody and @ControllerAdvice
// Returns a prompt that converts the entity class to JSON format, in line with the back-end separation architecture
@RestControllerAdvice
public class GlobalExceptionHandler {
// There is a custom BaseException, which is handled by this method when a BaseException is thrown
@ExceptionHandler(BaseException.class)
public ErrorInfo errorHandler(HttpServletRequest req, BaseException e) throws Exception {
ErrorInfo r = new ErrorInfo();
r.setMessage(e.getMessage());
r.setCode(ErrorInfo.ERROR);
r.setUrl(req.getRequestURL().toString());
returnr; }}Copy the code
When done, write a test interface:
@RequestMapping("/base")
public String base(String msg) throws Exception {
if ("base".equals(msg)) {
throw new BaseException("Test to throw BaseException, oh yeah!");
}
return "index";
}
Copy the code
Start the project, test:
omg
There’s definitely more to SpringMVC than what I’ve written, but once you’ve learned the above, you’re basically ready for everyday work.
If you want to go a little further, it is best to look at the source code of SpringMVC. I have written three previous articles, the Chain of Responsibility pattern and SpringMVC interceptor, the adapter pattern and SpringMVC, and the source code analysis of global exception handling. If you are interested, you can follow the official account to see my historical articles.
Wechat public account has been opened: [Java technology lovers], remember to pay attention to those who did not follow oh ~
Adhere to the original, continue to output both breadth and depth of technical articles.
This article has been included in my Github selection, welcome to Star: github.com/yehongzhi/l…
The code for all of the above examples is uploaded to Github:
Github.com/yehongzhi/m…
Please give me a thumbs-up if you think it is useful. Your thumbs-up is the biggest motivation for my creation
Refusing to be a salt fish, I’m a programmer trying to be remembered. See you next time!!
Ability is limited, if there is any mistake or improper place, please criticize and correct, study together!