###背景 应用中需要内置几个特殊字段(createUser,updateUser,createTime,updateTime,status,batchId,source,id)来记录数据导入的状态。要求这几个字段不和用户自定义的业务字段冲突,就考虑选择的内置字段名称要尽反人类一点,和常规的字段命名习惯相悖。
使用的开发技术照常是Java,SpringMVC,MyBatis。Java的变量命名规则: 变量名大小写敏感. 一个变量名字可以是任意合法的标示符,即一个不限长度的 Unicode字母、数字, 以字母, "$", 或 "_"开头。惯例以字母开头定义变量名 , 而不是 "$" 或 "_"。所以就考虑变量前后都缀上"_"。 然后就开始踩坑。
###以“_”开头 问题:后台接收请求数据后,在SpringMVC解析绑定参数时参数值丢失了。
原因:因为WebDataBind在解析绑定请求参数到变量之前,会做checkFieldDefaults(),checkFieldMarkers()。 默认地,CheckFieldDefaults会把前缀“!”的请求参数的值当作被前缀的参数的默认值,然后把把前缀“!”的请求参数删除。checkFieldMarkers会给被前缀"_"的请求参数在空值的状态下填上设定好的默认的"空值",然后把把前缀“_”的请求参数删除。2段代码如下:
protected void checkFieldDefaults(MutablePropertyValues mpvs) { if (getFieldDefaultPrefix() != null) { String fieldDefaultPrefix = getFieldDefaultPrefix(); PropertyValue[] pvArray = mpvs.getPropertyValues(); for (PropertyValue pv : pvArray) { if (pv.getName().startsWith(fieldDefaultPrefix)) { String field = pv.getName().substring(fieldDefaultPrefix.length()); if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) { mpvs.add(field, pv.getValue()); } mpvs.removePropertyValue(pv); } } }}protected void checkFieldMarkers(MutablePropertyValues mpvs) { if (getFieldMarkerPrefix() != null) { String fieldMarkerPrefix = getFieldMarkerPrefix(); PropertyValue[] pvArray = mpvs.getPropertyValues(); for (PropertyValue pv : pvArray) { if (pv.getName().startsWith(fieldMarkerPrefix)) { String field = pv.getName().substring(fieldMarkerPrefix.length()); if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) { Class fieldType = getPropertyAccessor().getPropertyType(field); mpvs.add(field, getEmptyValue(field, fieldType)); } mpvs.removePropertyValue(pv); } } }}
###以“$”开头 下划线不行,那就用$吧。SpringMVC是成功绑定获取到值了。但是接下来用Mybatis拼接sql时出错了。$开头的参数全部找不到getter方法,代码跟踪走起。发现mybatis使用org.apache.ibatis.reflection.Reflector
反射获取参数值。在初始化Reflector填充<参数,getter>映射获取getter的时,对getter方法进行过滤,规则如下:
private boolean isValidPropertyName(String name) { return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));}
过滤掉以$开头的getter。
###最后 后来就extends一下ConfigurableWebBindingInitializer,把WebDataBind的fieldMarkerPrefix设置成null。
也许是解决方案不对,才会用到反人类的变量命名,才会踩到坑。