介绍
前面几节我们介绍了Spring MVC的几种常见的数据绑定的方法,可以灵活地获取用户请求中的参数,例如@PathVariable,@ModelAttribute,@RequestParam等这些数据绑定注解,有了这些注解,我们可以很方便的去获取参数,但是偶尔我们需要自定义的去进行数据绑定,Spring一直遵循开闭原则,可以让使用者自定义去做自己的事情,今天我们一起浅析一下HandlerMethodArgumentResolver
认识HandlerMethodArgumentResolver,我们可以看下HandlerMethodArgumentResolver的继承关系图
我们可以很清楚的看到我们常见的几个数据绑定的annotation的具体实现都是实现了HandlerMethodArgumentResolver这个接口,我们可以先尝试一下写一个demo,自定义annotation绑定一个数据
首先我们新建一个annotation-----CurrentUser 默认只能注解参数,且运行时有效:
package org.study.lyncc.web.resolver.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CurrentUser { String value() default ""; }我们按照他的定义,自定义一个CurrentUserHandlerMethodArgumentResolver实现HandlerMethodArgumentResolver:
package org.study.lyncc.web.resolver; 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; import org.study.lyncc.web.entity.User; import org.study.lyncc.web.resolver.annotation.CurrentUser; public class CurrentUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver{ public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(CurrentUser.class); } public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { return new User(1,"lyncc"); } }
这边只是一个简单的demo,默认返回一个user,这样,我们写个controller来测试一下
package org.study.lyncc.web.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import org.study.lyncc.web.entity.User; import org.study.lyncc.web.resolver.annotation.CurrentUser; @Controller @RequestMapping(value="/data/binding") public class CostomDataBindingController { @RequestMapping(value="/test") public ModelAndView costomData(@CurrentUser User u){ System.out.println(u.getUsername()); ModelAndView mav = new ModelAndView("session"); return mav; } }我们写了一个测试方法,costomData中间的入参是User,前面加了我们自定义的注解@CurrentUser,这样此处的User应该就是我们在CurrentUserHandlerMethodArgumentResolver中resolveArgument方法返回的对象了
不过我们还需要让spring容器加载我们自定义的数据绑定的类CurrentUserHandlerMethodArgumentResolver,我们修改一下我们spring mvc的配置文件spring-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 基于注解驱动 --> <mvc:annotation-driven> <mvc:argument-resolvers> <bean class="org.study.lyncc.web.resolver.CurrentUserHandlerMethodArgumentResolver"/> </mvc:argument-resolvers> </mvc:annotation-driven> <!-- 默认spring的扫描包, 此处指定处理扫描org.study.lyncc.web.controller包下的类有@Controller注解的--> <context:component-scan base-package="org.study.lyncc.web.controller" > <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- 静态资源的处理 --> <mvc:resources mapping="/fonts/**" location="/WEB-INF/fonts/"/> <mvc:resources mapping="/js/**" location="/WEB-INF/js/"/> <!--视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>在<mvc:annotation-driven>中加入我们的自定义的类的全路径
启动spring mvc 浏览器中输入:http://localhost:8080/spring-mvc/data/binding/test,发现在控制台中正常打印了
好了,我们再举一个例子,加入我们的url是http://localhost:8080/spring-mvc/data/binding/custom?names=lyncc,fly,ted&ids=1,2,3,我们想要在后台直接获取一个List<User>
也就说我们可以提前转化
同样我们定义一个annotation
package org.study.lyncc.web.resolver.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ManyUser { String value() default ""; }ManyUserHandlerMethodArgumentResolver
package org.study.lyncc.web.resolver; import java.util.ArrayList; import java.util.List; 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; import org.study.lyncc.web.entity.User; import org.study.lyncc.web.resolver.annotation.ManyUser; public class ManyUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver{ public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(ManyUser.class); } public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { List<User> users = new ArrayList<User>(); String names = (String)webRequest.getParameter("names"); String ids = (String)webRequest.getParameter("ids"); if(null != names && null != ids){ String[] nameStrs = names.trim().split(","); String[] idStrs = ids.trim().split(","); for(int i = 0;i<nameStrs.length;i++){ User user = new User(Integer.parseInt(idStrs[i]), nameStrs[i]); users.add(user); } } return users; } }
package org.study.lyncc.web.controller; import java.util.List; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import org.study.lyncc.web.entity.User; import org.study.lyncc.web.resolver.annotation.CurrentUser; import org.study.lyncc.web.resolver.annotation.ManyUser; @Controller @RequestMapping(value="/data/binding") public class CostomDataBindingController { @RequestMapping(value="/test") public ModelAndView costomData(@CurrentUser User u){ System.out.println(u.getUsername()); ModelAndView mav = new ModelAndView("session"); return mav; } @RequestMapping(value="/custom") public ModelAndView customData1(@ManyUser List<User> users){ if(null != users && !users.isEmpty()){ for(User u : users){ System.out.println(u); } } ModelAndView mav = new ModelAndView("session"); return mav; } }别忘记修改spring-servlet.xml
<mvc:annotation-driven> <mvc:argument-resolvers> <bean class="org.study.lyncc.web.resolver.CurrentUserHandlerMethodArgumentResolver"/> <bean class="org.study.lyncc.web.resolver.ManyUserHandlerMethodArgumentResolver"/> </mvc:argument-resolvers> </mvc:annotation-driven>启动项目,输入http://localhost:8080/spring-mvc/data/binding/custom?names=lyncc,fly,ted&ids=1,2,3