采菊东篱下,悠然见南山。
何为HandlerMethodArgumentResolver?
Strategy interface for resolving method parameters into argument values in the context of a given request.
HandlerMethodArgumentResolver(处理程序方法参数解析器)这个类就是针对某一个方法列表的参数,对参数进行解析改造,使其得到你想要的结果。
示例思想
在许多业务场景下,我们都需要用到当前登录用户的个人信息。我们当然可以使用查询的方式获取,但是这种方式未免有些不够方便高效。所以我们可以使用HandlerMethodArgumentResolver构造自定义注解将用户信息注入到当前自定义好的用户类型的参数中来获取。
代码实现
项目结构
StartUp(启动类)
package demointerceptor;
import demointerceptor.interceptor.DemoInterceptor;
import demointerceptor.methodargumentresolver.CurrentUserMethodArgumentResolver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@SpringBootApplication
public class StartUp implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(StartUp.class,args);
}
//配置拦截器。
@Configuration
static class DemoInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");
}
}
//产生一个CurrentUserMethodArgumentResolver对象,
//然后这个CurrentUserMethodArgumentResolver对象交给Spring管理。
@Bean
public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
return new CurrentUserMethodArgumentResolver();
}
//配制参数解析器。
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(currentUserMethodArgumentResolver());
}
}
DemoLogin.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>登录示例</title>
</head>
<body>
<form action="http://localhost:8080/demologin" method="post">
<input type="text" name="userName"/>
<input type="text" name="userSex"/>
<input type="text" name="userAge"/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
DemoInterceptor.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>请求示例</title>
</head>
<body>
<form action="http://localhost:8080/demointerceptor" method="get">
<input type="submit" value="发送请求"/>
</form>
<h1></h1>
<form action="http://localhost:8080/showuserinfo" method="get">
<input type="submit" value="展示信息"/>
</form>
</body>
</html>
DemoInterceptor
package demointerceptor.interceptor;
import demointerceptor.cache.UserCache;
import demointerceptor.pojo.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 拦截器中方法的执行流程是 preHandle -> Controlelr -> postHandle -> afterCompletion。
*/
public class DemoInterceptor implements HandlerInterceptor {
/**
* 在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理.
* 如果设置为false时,被请求时,拦截器执行到此处将不会继续操作。
* 如果设置为true时,请求将会继续执行后面的操作。
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//do somthing.
System.out.println("请求已到达拦截器preHandle。");
System.out.println(request.getRequestURI());
String userName=UserCache.getFlagCache().get("FLAG");
if (userName != null){
User user=UserCache.getUserCache().get(userName);
request.setAttribute("currentUser",user);
}
return true;
}
/**
* 在业务处理器处理请求执行完成后,生成视图之前执行。
* 后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView。
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("请求已到达拦截器postHandle。");
}
/**
* 在DispatcherServlet完全处理完请求后被调用,
* 可用于清理资源等。返回处理(已经渲染了页面)。
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("请求已到达拦截器afterCompletion。");
}
}
DemoInterceptorController
package demointerceptor.controller;
import demointerceptor.annotations.CurrentUser;
import demointerceptor.cache.UserCache;
import demointerceptor.pojo.User;
import org.springframework.web.bind.annotation.*;
@RestController
public class DemoInterceptorController {
/**
* 模拟拦截。
*
*/
@GetMapping (value = "demointerceptor")
public void demoRequest(){
System.out.println("方法未被拦截。");
}
/**
* 登录。
*
*/
@PostMapping(value = "demologin")
public void demoLogin(@RequestParam String userName,
@RequestParam String userSex,
@RequestParam String userAge){
//身份验证省略。
//将登录用户信息放入缓存中。
User user=new User();
user.setUserName(userName);
user.setUserAge(userAge);
user.setUserSex(userSex);
UserCache.getUserCache().put(user.getUserName(),user);
//为了拦截器方法中获取User对象做准备。
UserCache.getFlagCache().put("FLAG",user.getUserName());
System.out.println("用户信息已放入缓存中。");
}
/**
* 信息展示。
* @param user
*/
@GetMapping(value = "showuserinfo")
public void showUserInfo(@CurrentUser User user){
System.out.println("用户名:"+user.getUserName());
System.out.println("性别:"+user.getUserSex());
System.out.println("年龄:"+user.getUserAge());
}
}
UserCache
package demointerceptor.cache;
import demointerceptor.pojo.User;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class UserCache {
private static Map<String, User> userCache =new ConcurrentHashMap<>();
private static Map<String, String> flagCache =new ConcurrentHashMap<>();
public static Map<String, User> getUserCache() {
return userCache;
}
public static Map<String, String> getFlagCache() {
return flagCache;
}
}
User
package demointerceptor.pojo;
/**
* 用户实体类。
*/
public class User {
private String userName;
private String userSex;
private String userAge;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserAge() {
return userAge;
}
public void setUserAge(String userAge) {
this.userAge = userAge;
}
}
CurrentUser
package demointerceptor.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解。
* 1.可用在方法的参数上。
* 2.运行时有效。
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {
}
CurrentUserMethodArgumentResolver
package demointerceptor.methodargumentresolver;
import demointerceptor.annotations.CurrentUser;
import demointerceptor.pojo.User;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
/**
* 自定义参数解析器,将含有 @CurrentUser 注解的方法参数注入当前登录用户。
*/
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
/**
*用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法resolveArgument。
* @param parameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().isAssignableFrom(User.class) &&
parameter.hasParameterAnnotation(CurrentUser.class);
}
/**
*真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象。
* @param parameter
* @param mavContainer
* @param webRequest
* @param binderFactory
* @return
* @throws Exception
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
User currentUser = (User) webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST);
if (currentUser!=null){
return currentUser;
}
throw new MissingServletRequestPartException("currentUser");
}
}
实现效果
首先用户登录,登录后调用信息展示方法。