11.1 参数解析器介绍
参数解析器属于spring-web包中提供的组件,springmvc框架中对应提供了很多参数解析器。例如我们开发的Controller代码如下:
1 2 3 4 5 6 7 8 9
| @RestController @RequestMapping("/user") public class UserController{ @PostMapping("/save") public String saveUser(HttpServletRequest request){ return "success"; } }
|
在上面的saveUser方法中,我们声明了一个类型为HttpServletRequest
的参数,这个对象就是通过springmvc提供的ServletRequestMethodArgumentResolver
这个参数解析器帮我们注入的。同样如果我们需要使用HttpServletResponse对象,也可以直接在方法上加入这个参数即可,此时springmvc会通过ServletResponseMethodArgumentResolver这个参数解析器帮我们注入。
在项目开发中我们也可以根据需要自定义参数解析器,需要实现HandlerMethodArgumentResolver
接口:
1 2 3 4 5 6 7 8 9
| public interface HandlerMethodArgumentResolver { boolean supportsParameter(MethodParameter var1);
@Nullable Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception; }
|
可以看到此接口包含两个接口方法:supportsParameter
和resolveArgument
。
当supportsParameter
方法返回true时,才会调用resolveArgument
方法。
11.2 参数解析器入门案例
本案例要实现的功能为:通过在Controller的方法参数上加入@CurrentUser注解来注入当前登录用户对象。
第一步:创建maven工程argumentResolver_demo并配置pom.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?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>
|
第二步:创建application.yml
第三步:创建User实体类
1 2 3 4 5 6 7 8 9 10 11 12
| 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; }
|
第四步:创建UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package cn.itcast.controller;
import cn.itcast.entity.User; import org.springframework.web.bind.annotation.*;
@RestController @RequestMapping(value = "/user") public class UserController { @GetMapping("/getCurrentUser") public String getCurrentUser(User user) { String name = user.getUsername(); System.out.println("UserController getCurrentUser方法..."); return user.toString(); } }
|
第五步:创建启动类
1 2 3 4 5 6 7 8 9 10 11
| 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); } }
|
此时可以启动项目并且访问:http://localhost:9000/user/getCurrentUser,可以发现虽然能够访问成功,但是user对象的属性都是空的。为了能够获得当前系统登录用户,我们可以通过Spring提供的参数解析器来实现。
第六步:创建CurrentUser注解
1 2 3 4 5 6 7 8 9 10 11 12
| package cn.itcast.anno;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CurrentUser { }
|
第七步:创建参数解析器类,需要实现HandlerMethodArgumentResolver接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 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;
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver { public CurrentUserMethodArgumentResolver() { System.out.println("CurrentUserMethodArgumentResolver自定义参数解析器初始化..."); }
@Override public boolean supportsParameter(MethodParameter parameter) { if (parameter.getParameterType().equals(User.class) && parameter.hasParameterAnnotation(CurrentUser.class)) { return true; } return false; }
@Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { System.out.println("参数解析器..."); User user = new User(1L,"admin"); return user; } }
|
第八步:创建配置类,用于注册自定义参数解析器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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 public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(getCurrentUserMethodArgumentResolver()); } }
|
第九步:修改UserController,在User参数前加入@CurrentUser注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 { @GetMapping("/getCurrentUser") public String getCurrentUser(@CurrentUser User user) { String name = user.getUsername(); System.out.println("UserController getCurrentUser方法..."); return user.toString(); } }
|
重新启动项目访问,发现user对象的属性已经有值了,这是因为我们在Controller方法的User参数前加入了@CurrentUser注解,在我们访问Controller的方法时Spring框架会调用我们自定义的参数解析器的supportsParameter方法来判断是否执行resolveArgument方法,如果Controller方法的参数类型为User并且加入了@CurrentUser注解则执行resolverArgument方法,此方法的返回结果将赋值给我们的Controller方法中声明的user参数,即完成了参数绑定。
pd-tools-user的实现和我们上面的入门案例是一致的,都是通过自定义参数解析器来为Controller的方法注入当前登录用户对象。
实现思路:
1、定义LoginUser注解,用于标注在Controller的方法参数上
2、自定义拦截器,从请求头中获取用户信息并设置到上下文(通过ThreadLocal实现)中
3、自定义参数解析器,从上下文中获取用户信息并封装为SysUser对象给Controller的方法参数
4、定义配置类,用于注册自定义拦截器和参数解析器
注意:pd-tools-user模块并不是starter,所以如果要使用其提供的功能,需要在应用的启动类上加入@EnableLoginArgResolver注解。
具体使用过程:
第一步:创建maven工程myCurrentUserApp并配置pom.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?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>com.itheima</groupId> <artifactId>myCurrentUserApp</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>com.itheima</groupId> <artifactId>pd-tools-user</artifactId> <version>1.0-SNAPSHOT</version> <exclusions> <exclusion> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
|
第二步:编写启动类
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.itheima;
import com.itheima.pinda.user.annotation.EnableLoginArgResolver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @EnableLoginArgResolver public class MyCurrentUserApplication { public static void main(String[] args) { SpringApplication.run(MyCurrentUserApplication.class,args); } }
|
第三步:创建UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.itheima.controller;
import com.itheima.pinda.user.annotation.LoginUser; import com.itheima.pinda.user.model.SysUser; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/user") public class UserController { @GetMapping("/getCurrentUser") public SysUser getCurrentUser(@LoginUser SysUser user){ System.out.println(user); return user; } }
|
启动项目,因为pd-tools-user模块需要从请求头中获取用户信息,所以需要使用postman进行测试:

可以通过debug断点调试的方式来跟踪程序的执行过程。