banner
NEWS LETTER

使用参数解析器完成对象的自动注入

Scroll down

11.1 参数解析器介绍

参数解析器属于spring-web包中提供的组件,springmvc框架中对应提供了很多参数解析器。例如我们开发的Controller代码如下:

1
2
3
4
5
6
7
8
9
@RestController
@RequestMapping("/user")
public class UserController{
@PostMapping("/save")
//此处request对象就是通过Springmvc提供的参数解析器帮我们注入的
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;
}

可以看到此接口包含两个接口方法:supportsParameterresolveArgument

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

1
2
server:
port: 9000

第三步:创建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) {
//如果Controller的方法参数类型为User同时还加入了CurrentUser注解,则返回true
if (parameter.getParameterType().equals(User.class) &&
parameter.hasParameterAnnotation(CurrentUser.class)) {
return true;
}
return false;
}

//当supportsParameter方法返回true时执行此方法
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
System.out.println("参数解析器...");
//此处直接模拟了一个User对象,实际项目中可能需要从请求头中获取登录用户的令牌然后进行解析,
//最终封装成User对象返回即可,这样在Controller的方法形参就可以直接引用到User对象了
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")
//注意:需要在User参数前加入CurrentUser注解
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参数,即完成了参数绑定。

11.3 pd-tools-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进行测试:

20231007224225

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

Other Articles