自定義HandlerMethodArgumentResolver參數解析器和源碼分析

在初學springmvc框架時,我就一直有一個疑問,爲何controller方法上居然能夠放這麼多的參數,並且都能獲得想要的對象,好比HttpServletRequest或HttpServletResponse,各類註解@RequestParam@RequestHeader@RequestBody@PathVariable@ModelAttribute等。相信不少初學者都曾經感慨過。java

這一章就是講解處理這方面工做的web

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

springmvc自帶的一些實現:json

  • 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的源碼,實現一些咱們本身的實現類,而方便咱們的代碼開發。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());
	}
}
相關文章
相關標籤/搜索