1.前言
Java开发中,多数接口需要用户登录才能执行业务逻辑,因此会有大量重复代码用于校验用户登录和获取用户信息,不免有些繁琐,也容易遗漏。
2.Spring如何实现请求参数注入
public class User {
private Long id;
private String name;
}
@GetMapping("param")
public String param(User user) {
System.out.println(user);
return "param";
}
Spring中,如果要获取queryString的参数,只要对Controller的方法添加同名的参数或者具有同名field的对象即可。这种方式既优雅,又隔离了HttpRequest的API,不失为一种妙计。
Spring实现这种优雅的注入方式,就依赖于HandlerMethodArgumentResolver这个扩展接口
3.扩展HandlerMethodArgumentResolver
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
HandlerMethodArgumentResolver定义了两个方法,supportsParameter用于判断是否要执行参数注入,resolveArgument则执行参数解析并注入到Controller方法。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface UserInfo {
boolean required() default true;
}
首先我们需要定义一个注解@UserInfo,用于标记我们要注入的对象,注解需要在运行时生效。@UserInfo有一个reqiured属性,标记了接口是否必须要用户登录才能访问,用于一些根据用户登录与否展示不同数据的接口。
@GetMapping("/required")
public String required(@UserInfo User user) {
System.out.println(user);
return "required";
}
@GetMapping("notRequired")
public String notRequired(@UserInfo(required = false) User user) {
System.out.println(user);
return "not required";
}
为Controller添加User参数,并用@UserInfo标记
public class Token2UserResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
//方法参数有@UserInfo标记
return parameter.hasParameterAnnotation(UserInfo.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//获取@UserInfo
UserInfo userInfo = parameter.getParameterAnnotation(UserInfo.class);
//从header获取access-token
String token = webRequest.getHeader("access-token");
if ((token == null || token.isEmpty())) {
if (userInfo.required()) {
//token不存在且必须登录访问的接口,抛出异常,配合统一异常处理返回相应状态码给前段
throw new RuntimeException("401");
} else {
//非必须登录则不处理
return null;
}
}
//将token解析为User对象
User user = null;
if (token.equals("token-a")) {
user = new User(1L, "user-A");
} else if (token.equals("token-b")) {
user = new User(2L, "user-B");
}
return user;
}
}
实现HandlerMethodArgumentResolver,这里简单模拟了一个token解析的过程,实际开发中可以用redis或者jwt之类的方案实现。
需要注意的是,这里完成的功能并非只有注入用户信息,还有强制接口登录的功能。@UserInfo的required属性为true时,如果没登录将抛出异常,配合统一异常处理可以实现访问控制和优雅的响应。
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new Token2UserResolver());
}
}
最后通过Spring提供的WebMvcConfigurer将Token2UserResolver添加到解析器链中。如此,一个优雅的用户信息解析工具就完成了。业务开发者便可以专注业务的实现,无需再操心用户信息的获取和校验了。