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

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. git 命令 clone分支的代码

    一个项目通常含有很多分支, master分支一般是经过测试,验证没有问题后,代码才会提交到master分支 develop分支,是测试经常拉下来进行测试的分支 直接复制develop分支的git 命令 ...

  2. [转]C和C++运行时库

    转自csdn原文:https://blog.csdn.net/ithzhang/article/details/20160009 图片请去原文查看 在使用VC构建项目时,经常会遇到下面的链接错误: 初 ...

  3. OpenModelica 在特定目录下生成仿真结果文件

    OMEdit的仿真结果文件存放在:C:\Users\***\AppData\Local\Temp\OpenModelica\OMEdit 可以在仿真时设置仿真结果文件名,可使用用绝对路径或相对路径 使 ...

  4. 从classloader的变更说起

    classloader从1.6到1.7整体分成了两个版本.重点区别就是并行类加载. 1.6版本 protected synchronized Class loadClass(String name, ...

  5. Windows的空格预览神器 | QuickLook

    用惯了Mac,发现空格预览真的能极大的提升效率和手感. QuickLook,Windows版的空格预览神器,效果差一些,速度有点慢,勉强用吧.

  6. spring ----> aop测试需要的Maven依赖/测试时发生的一个exception

    <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> &l ...

  7. Python自学:第二章 合并(拼接字符串)

    first_name = "ada" last_name = "lovelace" full_name = first_name + " " ...

  8. 关于如何获取Google 官方 NavgationView中的控件的方法

    最近在想要寻找一个好集成的SlidingMenu,看上了官方的DrawLayout,简单易集成 然后如果想动态改变DrawLayout头部的HeaderVIew的资源的话需要先获取到控件 方法如下 n ...

  9. Confluence 6 设置你的个人空间主页

    不论你是否正在使用个人空间为沙盒来测试一些内容,组合灯显示是如何工作的,一个能够导航到其他空间和内容的页面,或者一些完全不同的东西.下面一些红能够帮助你在你的个人空间中更加有效的使用和发布信息. 使用 ...

  10. Fiddler简介及web抓包

    1.Fiddler界面如下   2.Fiddler开关 界面左下角或点击F12控制Fiddler开关,开关是“Capturing”: 启动之后,Fiddler代理永远是开着的.     3.浏览器代理 ...