对于一些状态字段以前时兴用常量,现在时兴用枚举,虽然阅读体验极佳,但是传值的时候还是会有些麻烦,需要设置一下转换器.比如:

class A{
@Convert(converter=TestTypeConverter.class)
private TestType test; public TestType getTestType() {
return test;
} public void setTestType(TestType test) {
this.test= test;
}
}

我们定义了如上一个类,其中的一个成员变量是枚举,为了能正常的接收前端的值,一般会给这个枚举定义个转换器来实现String到枚举的转换.但是呢同事发现不定义这个转换器依然可以接收前端的值,这引起了我的兴趣,所以打算一探究竟..

项目使用的Spring推荐的Jackson作为json的编解码,因为前后端都是用json传值,所以这个问题也就转化成Jackson究竟做了什么内部的优化能达到不定义转换器的情况下依然可以正确的反序列化枚举.然后我们看一下这个示例的枚举TestType:

public enum TestType {
ZERO(0,"0页"),
ONE(1,"1页"),
TWO(2,"2页"); @JsonCreator
public static IsAllType get(int value) {
try {
return map.get(value);
} catch (Exception e) {
return null;
}
}
private int value;
private String text;
IsAllType(int value, String text){
this.value=value;
this.text=text;
}
@JsonValue
public int getValue() {
return value;
} }

示例,所以删减了部分无关代码.要debug跟踪,所以就通过json字符串转枚举这一过程来模拟前端传值这一操作,例子如下:

    public static void main(String[] args) {
A a = JSONUtils.readValue("{\"test\": \"1\"}", A.class);
}

经过一顿debug发现,Jackson会优先使用@JsonCreator注解定义的方法进行构造枚举值,由于定义了构造方法,某种意义上这就是一个简易的枚举转换器.

到此那个不定义converter就能接收值的问题看似已经结束,但是我的好奇心是很旺盛的..我把这个@JsonCreator注解注释掉看看会怎么样?然后发现依然可以正常接收值..这就有点意思了..又是一顿debug发现如下关键代码

    public static EnumResolver constructUsingMethod(Class<Enum<?>> enumCls,
Method accessor)
{
Enum<?>[] enumValues = enumCls.getEnumConstants();
HashMap<String, Enum<?>> map = new HashMap<String, Enum<?>>();
// from last to first, so that in case of duplicate values, first wins
for (int i = enumValues.length; --i >= 0; ) {
Enum<?> en = enumValues[i];
try {
Object o = accessor.invoke(en);
if (o != null) {
map.put(o.toString(), en);
}
} catch (Exception e) {
throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "+en+": "+e.getMessage());
}
}
return new EnumResolver(enumCls, enumValues, map);
}

jackson会构造出一个map,这个map的key是枚举值的value值,value是枚举类中对应的枚举值,然后通过这个map依然可以实现值到枚举类的转换.那么何为枚举值的value值?

    @JsonValue
public int getValue() {
return value;
}

jackson通过@JsonValue注解定义的方法返回值作为是枚举值的value值,通过这个value值又反向建立了关联,那我把这个@JsonValue注解也注释掉看看会怎么样?一运行发现还是可以接收值..

接着一顿debug发现如下关键代码:

    public static EnumResolver constructUsingToString(Class<Enum<?>> enumCls)
{
Enum<?>[] enumValues = enumCls.getEnumConstants();
HashMap<String, Enum<?>> map = new HashMap<String, Enum<?>>();
// from last to first, so that in case of duplicate values, first wins
for (int i = enumValues.length; --i >= 0; ) {
Enum<?> e = enumValues[i];
map.put(e.toString(), e);
}
return new EnumResolver(enumCls, enumValues, map);
}

通过获取枚举类中所有的枚举值,然后它构建了一个map,只不过这个map的key有点特殊,是枚举值的ordinal值,value为枚举值,所以此时如果枚举值的value是从0开始,也就是恰巧和ordinal值重合时,这样转换不会有问题,否则则会大错特错..debug的途中发现一些好玩的开发者留言..

    public static EnumResolver constructUnsafe(Class<?> rawEnumCls, AnnotationIntrospector ai)
{
/* This is oh so wrong... but at least ugliness is mostly hidden in just
* this one place.
*/
Class<Enum<?>> enumCls = (Class<Enum<?>>) rawEnumCls;
return constructFor(enumCls, ai);
} /**
* Method that needs to be used instead of {@link #constructUsingToString}
* if static type of enum is not known.
*/
@SuppressWarnings({ "unchecked" })
public static EnumResolver constructUnsafeUsingToString(Class<?> rawEnumCls)
{
// oh so wrong... not much that can be done tho
Class<Enum<?>> enumCls = (Class<Enum<?>>) rawEnumCls;
return constructUsingToString(enumCls);
} /**
* Method used when actual String serialization is indicated using @JsonValue
* on a method.
*/
@SuppressWarnings({ "unchecked" })
public static EnumResolver constructUnsafeUsingMethod(Class<?> rawEnumCls, Method accessor)
{
// wrong as ever but:
Class<Enum<?>> enumCls = (Class<Enum<?>>) rawEnumCls;
return constructUsingMethod(enumCls, accessor);
}

到此基本上我的好奇心也耗尽了...

总结: 1.jackson会优先使用@JsonCreator注解标注的构造方法构造枚举值

         2.jackson会通过@JsonValue注解标注的方法作为value值构建value与枚举值的map映射

         3.jackson会通过枚举值的ordinal值与枚举值构建map映射

ps:看来spring推荐jackson也并无道理,的确做了一些看不见的内部优化..

由@Convert注解引出的jackson对枚举的反序列化规则的更多相关文章

  1. @Convert 注解在jpa中进行查询的注意事项

    如果要实现实体类中属性的类型和数据库表中字段的类型相互转化,则需要使用 @Convert 注解 package javax.persistence; import java.lang.annotati ...

  2. @Data 注解引出的 lombok 小辣椒

    今天在看代码的时候, 看到了这个注解, 之前都没有见过, 所以就查了下, 发现还是个不错的注解, 可以让代码更加简洁. 这个注解来自于 lombok,lombok 能够减少大量的模板代码,减少了在使用 ...

  3. @Primary 注解引出的问题

    @Primary 注解 刚看到这个,还以为是持久层的注解呢,以为和@Id差不多,一查才知道,这两个风马牛不相及,反倒和@Qualifier以及@Resource有点像了,但是相比而言,后面两个更加的灵 ...

  4. Hibernate注解之@Enumerated:针对枚举enum(转)

    https://my.oschina.net/xinxingegeya/blog/359968 @Column(name = "store_type", nullable = fa ...

  5. [心得体会]springmvc在requestbody注解下使用jackson转化日期格式

    使用WebMvcConfigurer的方法将converter注入到项目中 @Configurationpublic class ConverterConfig implements WebMvcCo ...

  6. @Data 注解引出的 lombok

    今天在看代码的时候, 看到了这个注解, 之前都没有见过, 所以就查了下, 发现还是个不错的注解, 可以让代码更加简洁. 这个注解来自于 lombok,lombok 能够减少大量的模板代码,减少了在使用 ...

  7. 终极CRUD-3-用Jackson解析json

    目录 1 jackson json基本介绍和使用 2 jackson 常用的注解 2.1@JsonProperty 2.2 @JsonIgnore 2.3 @JsonIgnoreProperties ...

  8. java--加强之 jdk1.5简单新特性,枚举,注解

    转载请申明出处:http://blog.csdn.net/xmxkf/article/details/9944041 Jdk1.51新特性(静态导入,可变参数,加强for循环,自动拆装箱) 08.ja ...

  9. [Java] jackson注解

    Jackson(http://jackson.codehaus.org)库是基于java语言的开源json格式解析工具.相对于javajson解析的其他库,诸如json-lib.gson包,Jacks ...

随机推荐

  1. Spring boot 整合JSP开发步骤

    1. 新建Springboot项目,war <dependency> <groupId>org.springframework.boot</groupId> < ...

  2. Spring重要注解@ControllerAdvice

    @ControllerAdvice是一个@Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMa ...

  3. Windows.环境变量(设置)

    ZC: 我的示例代码(Delphi):http://www.cnblogs.com/CodeSkill/p/8341464.html 1.资料: 如何用代码设置环境变量?-CSDN论坛.html(ht ...

  4. 《剑指offer》第五十七题(为s的连续正数序列)

    // 面试题57(二):为s的连续正数序列 // 题目:输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数). // 例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以结 ...

  5. 《剑指offer》第四十二题(连续子数组的最大和)

    // 面试题42:连续子数组的最大和 // 题目:输入一个整型数组,数组里有正数也有负数.数组中一个或连续的多个整 // 数组成一个子数组.求所有子数组的和的最大值.要求时间复杂度为O(n). #in ...

  6. 大数据 - spark-sql 常用命令

    --spark启动 spark-sql --退出 spark-sql> quit; --退出spark-sql or spark-sql> exit; 1.查看已有的database sh ...

  7. Racadm设置idrac

    参考文档  idrac7-8-lifecycle-controller-v2.40.40.40_Reference Guide_en-us 0.下文中账户名密码均省略-r <RAC IP add ...

  8. 如何调节tomcat初始内存

    1.linux下调节tomcat初始内存大小 linux下tomcat的运行文件为catalina.sh,打开文件,在文件靠近顶部找到“ JAVA_OPTS ”字样,在它后面添加如下内容即可 # JA ...

  9. 使用VisualStudio进行脚本|样式文件压缩

    在vs的Optimization中有个Bundle是专门用来压缩样式和脚本文件 .他有两个继承:StyleBundle.ScriptBundle,从名字上就可看出,StyleBundle专门压缩样式文 ...

  10. 6 Django REST framework JWT 和登录功能实现

    JWT 在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证. 我们不再使用Session认证机制,而使用Json Web Token认证机制. Json web token ( ...