在初學springmvc框架時,我就一直有一個疑問,爲何controller方法上居然能夠放這麼多的參數,並且都能獲得想要的對象,好比HttpServletRequest或HttpServletResponse,各類註解@RequestParam
、@RequestHeader
、@RequestBody
、@PathVariable
、@ModelAttribute
等。相信不少初學者都曾經感慨過。java
這一章就是講解處理這方面工做的web
org.springframework.web.method.support.HandlerMethodArgumentResolver
接口。spring
springmvc自帶的一些實現:json
ServletRequestMethodArgumentResolver
和ServletResponseMethodArgumentResolver
處理了自動綁定HttpServletRequest和HttpServletResponseRequestParamMapMethodArgumentResolver
處理了@RequestParam
RequestHeaderMapMethodArgumentResolver
處理了@RequestHeader
PathVariableMapMethodArgumentResolver
處理了@PathVariable
ModelAttributeMethodProcessor
處理了@ModelAttribute
RequestResponseBodyMethodProcessor
處理了@RequestBody
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的源碼,實現一些咱們本身的實現類,而方便咱們的代碼開發。mvc
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; }
自定義實現app
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>
或者中配置的方式:ide
<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方式配置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()); } }