自定义HandlerMethodArgumentResolver参数解析器和源码分析
在初学springmvc框架时,我就一直有一个疑问,为什么controller方法上竟然可以放这么多的参数,而且都能得到想要的对象,比如HttpServletRequest或HttpServletResponse,各种注解@RequestParam
、@RequestHeader
、@RequestBody
、@PathVariable
、@ModelAttribute
等。相信很多初学者都曾经感慨过。
这一章就是讲解处理这方面工作的
org.springframework.web.method.support.HandlerMethodArgumentResolver
接口。
springmvc自带的一些实现:
ServletRequestMethodArgumentResolver
和ServletResponseMethodArgumentResolver
处理了自动绑定HttpServletRequest和HttpServletResponseRequestParamMapMethodArgumentResolver
处理了@RequestParam
RequestHeaderMapMethodArgumentResolver
处理了@RequestHeader
PathVariableMapMethodArgumentResolver
处理了@PathVariable
ModelAttributeMethodProcessor
处理了@ModelAttribute
RequestResponseBodyMethodProcessor
处理了@RequestBody
- 整体的spring实现为以下:
-
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters())); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters())); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters())); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
我们可以模仿springmvc的源码,实现一些我们自己的实现类,而方便我们的代码开发。
接口说明
package org.springframework.web.method.support; import org.springframework.core.MethodParameter; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; public interface HandlerMethodArgumentResolver { //用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法resolveArgument(可通过参数类型或注解等等)。 boolean supportsParameter(MethodParameter parameter); //真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象。 Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception; }
自定义实现
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; /** * 用于绑定@CurrentUser的方法参数解析器 * * @author lism */ public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver { public CurrentUserMethodArgumentResolver() { } @Override public boolean supportsParameter(MethodParameter parameter) { if (parameter.getParameterType().isAssignableFrom(UserBean.class) && parameter.hasParameterAnnotation(CurrentUser.class)) { return true; } return false; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { CurrentUser currentUserAnnotation = parameter.getParameterAnnotation(CurrentUser.class); //从Session 获取用户 Object object = webRequest.getAttribute(currentUserAnnotation.value(), NativeWebRequest.SCOPE_SESSION); //从 accessToken获得用户信息 if (object == null) { String token = webRequest.getHeader("Authorization"); if (token == null) { token = webRequest.getParameter("accessToken"); } //为了测试先写死用户名 //TODO: 取真实用户 return new UserBean(1L,"admin"); } return object; } }
import java.lang.annotation.*; /** * <p>绑定当前登录的用户</p> * <p>不同于@ModelAttribute</p> * * @author lism */ @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CurrentUser { /** * 当前用户在request中的名字 * * @return */ String value() default "user"; }
@RestController @RequestMapping(value = "/test") public class TestController { /** * 根据name查询 * * @param request * @return */ @RequestMapping(value = "/testCurrentUser", method = RequestMethod.POST, produces = "application/json", consumes = "application/json") @ResponseBody public void test(@CurrentUser UserBean userBean, @RequestBody SubjectRequest request) { String createdBy = userBean.getUsername(); log.info(createdBy); } }
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @NoArgsConstructor @AllArgsConstructor public class UserBean implements Serializable { private Long id; private String username; }
另外,我需要将他配置到spring context中。
<!--自定义controller接受参数进行解析 --> <mvc:annotation-driven> <mvc:argument-resolvers> <bean class="com.common.util.CurrentUserMethodArgumentResolver" /> </mvc:argument-resolvers> </mvc:annotation-driven>
或者中配置的方式:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="synchronizeOnSession" value="true" /> <property name="customArgumentResolvers"> <list> <bean class="com.common.util.CurrentUserMethodArgumentResolver" /> </list> </property> </bean>
spring-boot方式配置
package com.demo; import java.util.List; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import com.demo.mvc.component.MultiPersonArgumentResolver; import com.demo.mvc.component.PersonArgumentResolver; @SpringBootApplication public class WebMvcConfiguration extends WebMvcConfigurationSupport { @Override protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { // 注册Person的参数分解器 argumentResolvers.add(new PersonArgumentResolver()); } }