自定义HandlerMethodArgumentResolver参数解析器和源码分析

时间:2022-08-27 09:48:17

在初学springmvc框架时,我就一直有一个疑问,为什么controller方法上竟然可以放这么多的参数,而且都能得到想要的对象,比如HttpServletRequest或HttpServletResponse,各种注解@RequestParam@RequestHeader@RequestBody@PathVariable@ModelAttribute等。相信很多初学者都曾经感慨过。

这一章就是讲解处理这方面工作的

org.springframework.web.method.support.HandlerMethodArgumentResolver接口。

springmvc自带的一些实现:

  • ServletRequestMethodArgumentResolverServletResponseMethodArgumentResolver处理了自动绑定HttpServletRequest和HttpServletResponse
  • RequestParamMapMethodArgumentResolver处理了@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());
	}
}