HandlerMethodArgumentResolver接口
在SpringMVC开发中,控制层(Controller/Action)的 handler方法能够自动进行参数封装(比如@RequestParam、@PathVariable等注解),甚至在方法参数任意位置存在Http参数(如HttpServletRequest、HttpServletResponse、HttpSession、Writer等),都可以自动进行参数赋值。辣么,SpringMVC是怎么实现的呢?用的什么方法?
1. HandlerMethodArgumentResolver
策略接口:用于在给定 请求的上下文中 将方法参数解析为参数值。简单的理解为:它负责处理你Handler方法里的所有入参:包括自动封装、自动赋值、校验等等。有了它才能会让SpringMVC处理入参这么自动化。
SpringMVC内置了非常多HandlerMethodArgumentResolver接口实现,比如上面说到的RequestParamHandlerMethodArgumentResolver、PathVariableMapMethodArgumentResolver等,并且支持自定义。
自定义解析器需要实现HandlerMethodArgumentResolver接口,HandlerMethodArgumentResolver接口包含两个接口函数:
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
- supportsParameter:用于判定是否需要处理该参数分解(一般都是通过 参数上面的注解、参数的类型),返回true为需要,并会去调用下面的方法resolveArgument。
- resolveArgument:真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象。
resolveArgument参数
- 从NativeWebRequest中获取数据;
- ModelAndViewContainer用来提供访问Model;
- MethodParameter请求参数;
- WebDataBinderFactory用于创建一个WebDataBinder用于数据绑定、校验。
2. 自定义HandlerMethodArgumentResolver
使用HandlerMethodArgumentResolver给Controller注入用户信息。
2.1 创建实体类
/**
* 用户实体类
*/
public class UserBean {
private String id;
private String name;
private int age;
private String sex;
private String email;
private String phone;
public UserBean() {
}
public UserBean(String id, String name, int age, String sex, String email, String phone) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
this.email = email;
this.phone = phone;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "UserBean{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
2.2 创建标注注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 获取登录用户信息 annotation
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CaseUser {
}
2.3 创建HandlerMethodArgumentResolver类
import com.lizq.springboot.web.annotation.CaseUser;
import com.lizq.springboot.web.bean.UserBean;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
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;
@Component
public class CaseUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
if (methodParameter.getParameterType() == UserBean.class// 参数类型为UserBean
&& methodParameter.hasParameterAnnotation(CaseUser.class)// 参数标注注解为CaseUser
) {
return true;
}
return false;
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
// MethodParameter 请求参数信息
// ModelAndViewContainer 用来提供访问Model
// 从NativeWebRequest 中获取数据,如nativeWebRequest.getNativeRequest(XXX.class);nativeWebRequest.getNativeResponse(XXX.class);等
// WebDataBinderFactory 用于创建一个WebDataBinder用于数据绑定、校验
UserBean user = new UserBean("001", "张三", 24, "男", "test@163.com", "155xxxxx1688");
return user;
}
}
2.4 添加配置
2.4.1使用springboot添加
import org.springframework.beans.factory.annotation.Autowired;
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 WebConfiguration implements WebMvcConfigurer {
@Autowired
private CaseUserHandlerMethodArgumentResolver caseUserHandlerMethodArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
// 添加获取用户信息 HandlerMethodArgumentResolver
resolvers.add(caseUserHandlerMethodArgumentResolver);
}
}
2.4.2传统springmvc项目添加
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="xxx.xxx.xxx.CaseUserHandlerMethodArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
2.5 测试结果
@RequestMapping("testHandlerMethodArgumentResolver.do")
public void testHandlerMethodArgumentResolver(@CaseUser UserBean user1, UserBean user2,
@CaseUser String str1, String str2) {
System.out.println(user1.toString());
System.out.println(user2.toString());
System.out.println(str1);
System.out.println(str2);
}
打印结果:
3. 执行顺序问题
拦截器 ------>HandlerMethodArgumentResolver ------> AOP(@Aspect) ------>controller