Spring MVC源码解析:参数解析器,简化参数取值过程

在这里插入图片描述

参数解析器简化取值过程

参数解析器有什么作用呢?就是将你通过http提交的参数,转化成controller方法的参。超级方便,我们看看用Servlet开发是如何取参数值的?

@WebServlet(urlPatterns="/user")
public class UserServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userId = req.getParameter("userId");
        String token = req.getHeader("token");
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();
        out.println("userId " + userId + " token " + token);
    }
}

我们得自己手动从HttpServletRequest获取需要的信息,而在Spring MVC中只需要加上相应的参数注解即可,是不是超级方便!

@RestController
public class UserController {

	@RequestMapping("user")
	public String index(@RequestParam("userId") String userId,
						@RequestHeader("token") String token) {
		return "userId " + userId + " token " + token;
	}
}

Spring MVC中提供了很多这样的参数注解,方便了你的开发

javax.servlet.http.HttpServletRequest
javax.servlet.http.HttpServletResponse
javax.servlet.http.HttpSession
@PathVariable
@RequestParam
@RequestHeader
@RequestBody
@RequestAttribute

如果觉得Spring提供的参数解析器不能满足要求的话,我们还可以自定义参数解析器

自定义参数解析器

SpringMVC参数绑定的注解有很多,如@RequestParam,@RequestBody,@PathVariable,@RequestHeader,@CookieValue等。这些注解的实现方式很类似,都是有一个对应的解析器,解析完返回一个对象,放在方法的参数上。

如@RequestParam的解析器为RequestParamMethodArgumentResolver,@RequestBody的解析器为PathVariableMethodArgumentResolver等。假如说现在有一个场景,前端每次从前面传入一个userId,而后端呢,每次都去查数据库,然后拿到用户的信息。如果有很多个controller,每个controller上来都是一样的逻辑,去查数据库,然后拿用户信息,这样的代码就很烂。如何精简呢?答案就是自定义注解实现参数绑定

定义注解

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {

}

自定义参数解析器

参数解析器接口定义如下,总共有2个方法。supportsParameter判断是否支持该参数,如果支持该参数则通过resolveArgument把参数值解析出来

HandlerMethodArgumentResolver

public interface HandlerMethodArgumentResolver {

	// 是否支持该参数
	boolean supportsParameter(MethodParameter parameter);


	// 解析参数值
	@Nullable
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}
public class CurrentUserUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    /** 用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法resolveArgument。*/
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().isAssignableFrom(User.class) && parameter.hasParameterAnnotation(CurrentUser.class);
    }

    /** 真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象。*/
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
                                  NativeWebRequest request, WebDataBinderFactory factory) throws Exception {

        String userId = request.getParameter("userId");

        // 为了方便不模拟查数据库的过程,直接new一个username和password都是userId的对象
        return new User(userId, userId);
    }
}

加入配置

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new CurrentUserUserHandlerMethodArgumentResolver());
    }
}

测试controller

@RestController
public class UserController {

    @RequestMapping(value = "userInfo", method = RequestMethod.GET)
    public Map<String, String> userInfo(@CurrentUser User user) {
        Map<String, String> result = new HashMap<>();
        result.put("username", user.getUsername());
        result.put("password", user.getPassword());
        return result;
    }
}

访问: http://localhost:8080/userInfo?userId=1 结果为
在这里插入图片描述
我这里举的是一个很简单的例子,在方法上直接拿用户的信息,方便大家理解。开发过程中的需求比这个复杂很多,大家可以发挥想象力应用参数绑定注解,如判断用户是否登录,将前端传过来的数据直接转成一个List之类,放到方法的参数上面等。

源码解析

我们来看一下参数解析器是如何设置的

RequestMappingHandlerAdapter#afterPropertiesSet
在这里插入图片描述
在这里插入图片描述
在RequestMappingHandlerAdapter的初始化阶段,初始化默认的参数解析器和用户自定义的参数解析器

InvocableHandlerMethod#invokeForRequest
在这里插入图片描述

在这里插入图片描述
resolvers是HandlerMethodArgumentResolverComposite对象,组合了多个HandlerMethodArgumentResolver,如果没有对应的参数解析器可以解析则报错,否则用对应的参数解析器解析值

我们常用的参数对应的参数解析器如下,源码不负责,就不解析了

参数类型参数解析器
@PathVariablePathVariableMethodArgumentResolver
@RequestParamRequestParamMethodArgumentResolver
@RequestHeaderRequestHeaderMethodArgumentResolver
@RequestBodyRequestResponseBodyMethodProcessor
HttpServletRequestServletRequestMethodArgumentResolver
HttpServletResponseServletResponseMethodArgumentResolver

RequestResponseBodyMethodProcessor实现了HandlerMethodArgumentResolver接口和HandlerMethodReturnValueHandler接口

HandlerMethodArgumentResolver(参数解析器)接口用来处理@RequestBody注解,
HandlerMethodReturnValueHandler(返回值处理器)接口用来处理@ReponseBody注解

参考博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java识堂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值