1
头图
Like it first, then watch it, develop a good habit

background

When doing an old project refactoring before, because the database cannot be changed, we still continue to use the previous old database. For insurance companies, even if you add an Internet insurance title, the business and system are still traditional, and the data model will not be easily updated; so this system is relatively old, and its database table naming method is still Hungarian nomenclature , which made me sick for a long time because of this naming method when refactoring...

Hungarian nomenclature

Hungarian notation (Hungarian notation) , invented by programmer Charles Simeone who worked at Xerox Palo Alto Research Center from 1972 to 1981, this predecessor became Microsoft's chief designer .

This feature nomenclature, the increase in the naming front types of prefixes , like this:

  • c_name -name, character string (Char) type
  • n_age -age, number type
  • t_birthday -birthday, date/time (Time) type

Don't underestimate this nomenclature, it was very popular back then, and some systems still use this nomenclature today, such as Microsoft's Win32 API:
image.png
Moreover, this nomenclature is not useless, it still has certain advantages, at least I can see the type of this field at a glance.

It just looks a little weird today, not in line with today’s design style, it’s even more interesting if you put it on the name of the person:

  • male Zhao Si
  • Female Xie Dajia
  • male Liu Neng

When Hungarian nomenclature meets JAVA

Our goal of refactoring this time is to completely rewrite it while keeping the old system intact. However, the new system is developed in the Java language. Java is a camel case naming standard. What happens when the Hungarian nomenclature table is migrated to the Java language of the camel case nomenclature?

For example, after the name c_name is in Java, should it be changed to CName or cName? It seemed a little strange, but in the end we chose CName and capitalized the prefix of the type. At least it looked a little normal, not so anti-human.

  • c_name -> CName
  • n_age -> NAge
  • t_birthday -> TBirthday

Serialization problem

I just decided on the naming method and haven’t been happy for a long time, I ran into a very uncomfortable problem...

Since it is a Spring family bucket, the web layer also uses Spring MVC. The default JSON processing library of Spring MVC is Jackson. After the JSON is returned from the Web layer, the data becomes like this:

{
    "nid":1,
    "ctitle":"Effective JAVA"
}

But my POJO class has converted the Hungarian nomenclature to uppercase, it looks like this:

public class Book {
    private Integer NId;
    private String CTitle;

    public Integer getNId() {
        return NId;
    }

    public void setNId(Integer NId) {
        this.NId = NId;
    }

    public String getCTitle() {
        return CTitle;
    }

    public void setCTitle(String CTitle) {
        this.CTitle = CTitle;
    }
}

The uppercase field name becomes lowercase after converting to JSON... If this lowercase field is given to the front end, the front end name will definitely be lowercase, and the front end must also be lowercase when it is sent to the back end.

Then because our input and output parameters are serialized by Jackson, for Jackson, it can be parsed as it is out. It seems that there is no problem, but it is a bit disgusting. The front and back ends are uppercase and lowercase.

But... things are not that simple. The backend will not treat all messages as POJO fields, there will be some dynamic fields, which are stored in Map. But for these fields stored in Map, Jackson will not be converted to lowercase when outputting, but the original uppercase form will be retained. This will lead to the front-end fields, although they are all Hungarian style, but some uppercase and some lowercase...Although the front-end does not I know if I can hit people, but I don’t dare to play like that

Different serialization libraries have different processing mechanisms

Jackson's Hungarian nomenclature processing

In fact, the reason why Jackson changed to lowercase after serialization is also easy to explain. The Java design specification is that the properties in the Bean are decorated with private, and then getter/setter is provided to provide read/write. Then during serialization, Jackson uses the Getter of the field to access the property value, and even uses the Getter method to parse the property name.

Java's getter method naming standard is to convert lower case camel case to upper case case. Jackson parsed getNID as nid when parsing field names through the getter method name, so the final output field name was lower case nid.

FastJson's Hungarian nomenclature processing

Originally I wanted to customize Jackson's naming process, but after thinking about it, I felt unnecessary. After all, our naming is not standard. Why bother to change the naming process? It's not worth it.

So I tried another JSON library to deal with this naming problem, first try Ali's FastJSON to see how this domestic library handles:

{
    "cTitle":"Effective JAVA",
    "nId":1
}

When I saw the serialization result, I almost broke the Backspace on my keyboard... The property name is also parsed through the getter method. The parsing rules of the two libraries can be different...

In FastJson, c is lowercase, but T in Title is still uppercase, @#¥%……&100 characters are omitted here……

After calming down, I muttered silently several times in my heart: "Don't blame others, it's our own naming problem. It doesn't matter how you analyze it if you don't meet the standard..."

However, the ecology of JAVA is so good, there are more than these two JSON libraries, and another one is that Google's Gson is also very good!

Gson's Hungarian nomenclature

So I changed to Gson again, after configuring Spring MVC Gson Converter, the output result:

{
    "NId":1,
    "CTitle":"Effective JAVA"
}

Finally, I switched to a JSON library that can display the original field names normally, but since it can keep the original field names instead of the property names parsed in the getter, it must not parse the getter method name

So I went through the source code of Gson and found that it is directly getDeclaredFields() , then makeAccessible , and finally directly obtain the attribute value Field.getValue

Compared with Jackson and FastJson, which use getter to get the property list, and then call the getter method to get the property value, the method of forcing access to private properties is still too violent, but I like it, at least it can easily solve mine problem

Other serialization issues

In addition to the textual serialization of JSON, some binary serializations will also have this embarrassing problem. To obtain the property list/property value, is it to parse the getter method or directly makeAccessible to violently access private properties?

I tested this, for example, in Dubbo's default serialization method (Dubbo simplified Hession2), it is still getDeclaredFields , and then access private properties

In JDK's built-in serialization ObjectOutputStream, it is also getDeclaredFields , and then access private properties.

However, this way of getDeclaredFields and then accessing private attribute values also has some disadvantages. For example, when encountering code obfuscation, the values of private properties will be disrupted, but the public method will not. Therefore, when encountering obfuscated code, this method will be messed up, and it will be parsed through getter methods. There will be no problems.

So, there is no right or wrong way to get properties, it can be anything, but I think it should be operated by getter/setter, which conforms to JAVA specifications.

supplement

Thanks @ 3,323,102,545,477 user reminder, Jackson is very strong, supported configuration properties acquisition mode, you can configure Visibility be achieved only by Field not through getter to get property:

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
        getterVisibility = JsonAutoDetect.Visibility.NONE)
public class Book {
    private Integer NId = 1;

    private String CName = "John";
}

Global configuration is more convenient:

ObjectMapper objectMapper = new ObjectMapper();

// 配置 field 为可见
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

// 配置 getter 不可见
objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);

to sum up

The standard way to access private property values in Java is through the getter method, but it still provides a makeAccessible operation that allows us to directly access private properties or private methods.

I have never understood why the JDK is designed this way. Since the specifications have been specified, why open a backdoor? If this function is restricted, then all serialized libraries can not be unified, and there will be no such disgusting inconsistency problem!

But comparing the above three serialization libraries, I think they are all right. Jackson/FastJson follow the standard way and honestly obtain them through the getter method, while Gson is a bit violent and directly accesses private properties, each has its own advantages.

Supplement: Jackson supports the acquisition method of attributes. The default is to getter , but it can also be configured through Field . For the configuration method, see the supplement section above.

Originality is not easy, unauthorized reprinting is prohibited. If my article is helpful to you, please like/favorite/follow to encourage and support it ❤❤❤❤❤❤

空无
3.3k 声望4.3k 粉丝

坚持原创,专注分享 JAVA、网络、IO、JVM、GC 等技术干货