Controller方法的参数类型能够是基本类型,也能够是封装后的普通Java类型。若这个普通Java类型没有声明任何注解,则意味着它的每个属性都须要到Request中去查找对应的请求参数。众所周知,不管客户端传入的是什么类型的请求参数,最终都要以字节的形式传给服务端。而服务端经过Request的getParameter方法取到的参数也都是字符串形式的结果。因此,须要有一个把字符串形式的参数转换成服务端真正须要的类型的转换工具,在spring中这个转换工具为WebDataBinder。
WebDataBinder不须要咱们本身去建立,咱们只须要向它注册参数类型对应的属性编辑器PropertyEditor。PropertyEditor能够将字符串转换成其真正的数据类型,它的void setAsText(String text)方法实现数据转换的过程。
具体的作法是,在Controller中声明一个InitBinder方法,方法中利用WebDataBinder将本身实现的或者spring自带的PropertyEditor进行注册。像下面这样:
- @InitBinder
- public void initBinder(WebDataBinder binder) throws Exception {
- binder.registerCustomEditor(Long.class, new CustomNumberEditor(Long.class, true));
- binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
- }
处理没有任何注解的普通Java类型的参数解析器是ModelAttributeMethodProcessor,下面是参加解析方法的代码:
- public final Object resolveArgument(
- MethodParameter parameter, ModelAndViewContainer mavContainer,
- NativeWebRequest request, WebDataBinderFactory binderFactory)
- throws Exception {
-
- String name = ModelFactory.getNameForParameter(parameter);
- Object target = (mavContainer.containsAttribute(name)) ?
- mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
-
- WebDataBinder binder = binderFactory.createBinder(request, target, name);
- if (binder.getTarget() != null) {
- bindRequestParameters(binder, request);
- validateIfApplicable(binder, parameter);
- if (binder.getBindingResult().hasErrors()) {
- if (isBindExceptionRequired(binder, parameter)) {
- throw new BindException(binder.getBindingResult());
- }
- }
- }
-
- mavContainer.addAllAttributes(binder.getBindingResult().getModel());
- return binder.getTarget();
- }
每次请求到来后的参数解析都会利用WebDataBinderFactory建立一个binder对象,而后从这个binder中取得最终解析好的参数对象。WebDataBinderFactory是在InvocableHandlerMethod中定义的,即不一样的Controller方法有着不一样的WebDataBinderFactory。其实建立binder的同时还对binder进行了初始化,这个初始化过程就会执行Controller中的InitBinder方法。InitBinderDataBinderFactory实现了初始化binder的方法:
- public void initBinder(WebDataBinder binder, NativeWebRequest request) throws Exception {
- for (InvocableHandlerMethod binderMebinderMethod thod : this.binderMethods) {
- if (isBinderMethodApplicable(binderMethod, binder)) {
- Object returnValue = binderMethod.invokeForRequest(request, null, binder);
- if (returnValue != null) {
- throw new IllegalStateException("@InitBinder methods should return void: " + binderMethod);
- }
- }
- }
- }
上面方法中的binderMethods就是在Controller中定义的InitBinder方法,而且binderMethod 同Controller中的其余方法同样也是InvocableHandlerMethod。从上面的代码能够看出,InitBinder方法能够声明多个,WebDataBinderFactory初始化binder的时候会分别调用每一个InitBinder方法。而咱们在初始化的过程当中使用了binder.registerCustomEditor,间接地向BeanWrapperImpl中注册了传入的PropertyEditor,以便在参数类型转换的时候使用。
还记得刚才的ModelAttributeMethodProcessor解析参数时,建立binder以后调用了bindRequestParameters实现了请求参数的绑定,它的子类ServletModelAttributeMethodProcessor重写了这个方法:
- protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
- ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
- ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
- servletBinder.bind(servletRequest);
- }
不管是父类仍是子类,其实都是调用了binder的bind方法。下面是ServletRequestDataBinder的bind方法
- public void bind(ServletRequest request) {
- MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
- MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
- if (multipartRequest != null) {
- bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
- }
- addBindValues(mpvs, request);
- doBind(mpvs);
- }
这个方法跟依赖注入的过程很是类似,依赖注入是根据属性在容器中找到知足条件的对象,而后设置到当前的bean中。而上面的方法不是在容器中查找,而是从Request中获取,即把Request中的请求参数注入到binder的target中去。此时进行类型转换的就是刚刚注册的PropertyEditor,由于InitBinder方法每次都会执行,因此使用者能够在每一个Controller中对相同类型的参数定义不一样的参数转换方式。
通过了bindRequestParameters方法的处理,如今binder中target(即HandlerMethod的参数)已经包含了Request中的请求参数。
那么,如今还有一个问题,InvocableHandlerMethod中的WebDataBinderFactory是如何来的呢?它的建立过程在RequestMappingHandlerAdapter(本文全部逻辑过程均假定使用RequestMappingHandlerAdapter):
- private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
- Class<?> handlerType = handlerMethod.getBeanType();
- Set<Method> methods = this.dataBinderFactoryCache.get(handlerType);
- if (methods == null) {
- methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
- this.dataBinderFactoryCache.put(handlerType, methods);
- }
- List<InvocableHandlerMethod> binderMethods = new ArrayList<InvocableHandlerMethod>();
- for (Method method : methods) {
- InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
- binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
- binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
- binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
- binderMethods.add(binderMethod);
- }
- return createDataBinderFactory(binderMethods);
- }