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;
}
}