Spring MVC 自定义 PropertyEditor

image

PropertyEditor 隶属于java.beans包,允许用户编辑给定类型的属性值。

PropertyEditorRegistry 隶属于org.springframework.beans包,是PropertyEditor的注册表,提供了注册及寻找接口。

DataBinder 是PropertyEditorRegistry的具体实现。

接下来看看是findCustomerEditor是什么时候调用的?

package org.springframework.beans;
class TypeConverterDelegate {
    @Nullable
    public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
            @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
        // Custom editor for this type?
        // (1)
        PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
        
        // Value not of required type?
        // (2)
        if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
            if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
                    convertedValue instanceof String) {
                TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
                if (elementTypeDesc != null) {
                    Class<?> elementType = elementTypeDesc.getType();
                    if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
                        convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
                    }
                }
            }
            if (editor == null) {
                editor = findDefaultEditor(requiredType);
            }
            convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
        }       
    }
}

(1) 寻找自定义PropertyEditor
(2) 如果找到自定义的PropertyEditor, 就采用该Editor进行Value转换。

所以只要我们注入自定义的PropertyEditor到注册表即可,那如何注入呢?

这里要用到Spring MVC里数据绑定的一个主要类,WebDataBinder

具体案例如下,要求对字符串进行去空格处理,使用于请求地址中的请求参数

定义自定义PropertyEditor

public class CustomStringPropertyEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        if (text != null) {
            text = text.trim();
        }
        super.setValue(text);
    }
}

public class CustomListPropertyEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        if (text == null) {
            super.setAsText(null);
        }
        String[] source = text.split(",");
        String[] target = new String[source.length];
        for (int i = 0; i < source.length; i++) {
            target[i] = source[i].trim();
        }
        super.setValue(Arrays.asList(target));
    }
}

public class CustomArrayPropertyEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        if (text == null) {
            super.setAsText(null);
        }
        String[] source = text.split(",");
        String[] target = new String[source.length];
        for (int i = 0; i < source.length; i++) {
            target[i] = source[i].trim();
        }
        super.setValue(target);
    }
}

定义了三个PropertyEditor,分别支持List,String,Array.

注入自定义PropertyEditor

@RestController
public class HomeController {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(String.class, new CustomStringPropertyEditor());
        binder.registerCustomEditor(List.class, new CustomListPropertyEditor());
        binder.registerCustomEditor(java.lang.String[].class, new CustomArrayPropertyEditor());
    }

    @GetMapping("/testString")
    public void testList(@RequestParam(value = "a") String a) {
        System.out.println(":" + a + ":");
    }

    @GetMapping("/testList")
    public void testList(@RequestParam("names") List<String> params) {
        System.out.println(":" + String.join(",", params) + ":");
    }

    @GetMapping("/testArray")
    public void testArray(@RequestParam("names") String[] params) {
        System.out.println(":" + String.join(",", params) + ":");
    }

}

至此,整个自定义PropertyEditor功能完成

下面简单的整理下整个源码阅读的执行过程

package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        // 这里的ha的实际类是 RequestMappingHandlerAdapter
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    }
}

package org.springframework.web.servlet.mvc.method.annotation;
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {
    protected ModelAndView handleInternal(HttpServletRequest request, 
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        if (this.argumentResolvers != null) {
            // HandlerMethodArgumentResolverComposite
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            // HandlerMethodReturnValueHandlerComposite
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        // ServletRequestDataBinderFactory
        invocableMethod.setDataBinderFactory(binderFactory);
        // DefaultParameterNameDiscoverer
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
        // 调用ServletInvocableHandlerMethod处理请求
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
    }
}
            
package org.springframework.web.servlet.mvc.method.annotation;
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    }
}

public class InvocableHandlerMethod extends HandlerMethod {
    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    }
    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
        for (int i = 0; i < parameters.length; i++) {
            // 这个resolvers是否还有印象,就是HandlerMethodReturnValueHandlerComposite, 进行参数解析
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        }
    }
}

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" +
                    parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        }
        // 调用RequestParamMethodArgumentResolver进行参数解析
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
}

public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        // WebDataBinder 我们注入自定义PropertyEditor的关键类
        WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
        try {
            // 进行转换
            arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
        }
    }
}

package org.springframework.validation;
public class DataBinder implements PropertyEditorRegistry, TypeConverter {
    public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
            @Nullable MethodParameter methodParam) throws TypeMismatchException {
        // 调用TypeConverter进行转换
        return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
    }
}

package org.springframework.beans;
public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
    public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
            @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
        // 调用TypeConverterDelegatel来转换
        // 思考:为何要另起一个类来转换???
        return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
    }
}

package org.springframework.beans;
class TypeConverterDelegate {
    public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
            @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
        // 重点:根据类型,获取Custome Property Editor
        PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
        
        // Value not of required type?
        if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
            convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
        }
    }
    private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,
            @Nullable Class<?> requiredType, @Nullable PropertyEditor editor) {
        if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
            // Convert String array to a comma-separated String.
            // Only applies if no PropertyEditor converted the String array before.
            // The CSV String will be passed into a PropertyEditor's setAsText method, if any.
            if (logger.isTraceEnabled()) {
                logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");
            }
            convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
        }

        if (convertedValue instanceof String) {
            if (editor != null) {
                // Use PropertyEditor's setAsText in case of a String value.
                if (logger.isTraceEnabled()) {
                    logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");
                }
                String newTextValue = (String) convertedValue;
                return doConvertTextValue(oldValue, newTextValue, editor);
            }
            else if (String.class == requiredType) {
                returnValue = convertedValue;
            }
        }

        return returnValue;
    }
}

转载于:https://www.cnblogs.com/loveyx/p/11451095.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值