Spring MVC parameter parser

Keywords: Spring MVC

The parameter parser is a component provided in the spring web package. Many parameter parsers are provided in the spring MVC framework. For example, the Controller code we developed is as follows:

@RestController
@RequestMapping("/user")
public class UserController{
    @PostMapping("/save")
    //Here, the request object is injected through the parameter parser provided by spring MVC
    public String saveUser(HttpServletRequest request){
        return "success";
    }
}

In the above saveUser method, we declare a parameter of type HttpServletRequest, which is injected through the parameter parser ServletRequestMethodArgumentResolver provided by spring MVC. Similarly, if we need to use the HttpServletResponse object, we can also directly add this parameter to the method. At this time, springmvc will inject it for us through the parameter parser ServletResponseMethodArgumentResolver.

In the project development, we can also customize the parameter resolver as needed. We need to implement the HandlerMethodArgumentResolver interface:

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter var1);

    @Nullable
    Object resolveArgument(MethodParameter var1, 
                            @Nullable ModelAndViewContainer var2, 
                            NativeWebRequest var3, 
                            @Nullable WebDataBinderFactory var4) throws Exception;
}

You can see that this interface contains two interface methods: supportsParameter and resolveArgument.

The resolveArgument method is called only when the supportsprarameter method returns true.

Introduction to parameter parser

The function to be realized in this case is to inject the currently logged in user object by adding the @ CurrentUser annotation to the method parameters of the Controller.

Step 1: create maven project argumentResolver_demo and configure pom.xml file

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>cn.itcast</groupId>
    <artifactId>argumentResolver_demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

Step 2: create application.yml

server:
  port: 9000

Step 3: create User entity class

package cn.itcast.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;

@Data
@AllArgsConstructor
public class User implements Serializable {
    private Long id;
    private String username;
}

Step 4: create UserController

package cn.itcast.controller;

import cn.itcast.entity.User;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/user")
public class UserController {
    //Get current system login user
    @GetMapping("/getCurrentUser")
    public String getCurrentUser(User user) {
        String name = user.getUsername();
        System.out.println("UserController getCurrentUser method...");
        return user.toString();
    }
}

Step 5: create a startup class

package cn.itcast;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ArgumentResolverApp {
    public static void main(String[] args) {
        SpringApplication.run(ArgumentResolverApp.class,args);
    }
}

You can start the project and access: http://localhost:9000/user/getCurrentUser
It can be found that although the access is successful, the properties of the user object are empty. In order to obtain the current system login user, we can implement it through the parameter parser provided by Spring.

Step 6: create the CurrentUser annotation

package cn.itcast.anno;

import java.lang.annotation.*;

/**
* Bind current login user
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurrentUser {
}

Step 7: to create a parameter parser class, you need to implement the HandlerMethodArgumentResolver interface

package cn.itcast.resolver;

import cn.itcast.anno.CurrentUser;
import cn.itcast.entity.User;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
 * Custom parameter parser
 */
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
    public CurrentUserMethodArgumentResolver() {
        System.out.println("CurrentUserMethodArgumentResolver Custom parameter parser initialization...");
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        //If the method parameter type of the Controller is User and the CurrentUser annotation is added, true is returned
        if (parameter.getParameterType().equals(User.class) &&
                parameter.hasParameterAnnotation(CurrentUser.class)) {
            return true;
        }
        return false;
    }

    //This method is executed when the supportsParameter method returns true
    @Override
    public Object resolveArgument(MethodParameter parameter, 
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, 
                                  WebDataBinderFactory binderFactory) throws Exception {
        System.out.println("Parameter parser...");
        //A User object is directly simulated here. In the actual project, it may be necessary to obtain the login User's token from the request header and then parse it,
        //Finally, it can be encapsulated into a User object and returned, so that the method parameters in the Controller can directly reference the User object
        User user = new User(1L,"admin");
        
        return user;
    }
}

Step 8: create a configuration class to register the custom parameter parser

package cn.itcast.config;

import cn.itcast.resolver.CurrentUserMethodArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;

@Configuration
public class ArgumentResolverConfiguration implements WebMvcConfigurer {
    public CurrentUserMethodArgumentResolver getCurrentUserMethodArgumentResolver(){
        return new CurrentUserMethodArgumentResolver();
    }

    @Override
    //Register custom parameter parser
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(getCurrentUserMethodArgumentResolver());
    }
}

Step 9: modify UserController and add @ CurrentUser annotation before User parameter

package cn.itcast.controller;

import cn.itcast.anno.CurrentUser;
import cn.itcast.entity.User;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/user")
public class UserController {
    //Get current system login user
    @GetMapping("/getCurrentUser")
    //Note: the CurrentUser annotation needs to be added before the User parameter
    public String getCurrentUser(@CurrentUser User user) {
        String name = user.getUsername();
        System.out.println("UserController getCurrentUser method...");
        return user.toString();
    }
}

Restart the project access and find that the attribute of the user object has a value. This is because we added the @ CurrentUser annotation before the user parameter of the Controller method. When we access the Controller method, the Spring framework will call the supportsParameter method of our custom parameter parser to determine whether to execute the resolveArgument method, If the parameter type of the Controller method is user and the @ CurrentUser annotation is added, the resolverArgument method is executed, and the return result of this method will be assigned to the user parameter declared in our Controller method, that is, the parameter binding is completed.

Posted by manchuwok on Sun, 05 Dec 2021 16:58:01 -0800