Это второй день моего участия в Gengwen Challenge, чтобы узнать подробности о мероприятии, пожалуйста, проверьте:Обновить вызов
Ставь лайк и потом смотри, вырабатывай полезную привычку
задний план
Когда я ранее рефакторил старый проект, поскольку базу данных нельзя было изменить, я все равно продолжал использовать старую базу данных. Для страховых компаний, даже если будет добавлено название интернет-страхования, бизнес и система по-прежнему остаются традиционными, и модель данных не будет легко обновляться; поэтому эта система относительно старая, и ее метод именования таблиц базы данных все ещеВенгерская номенклатура, от которого меня долго тошнило из-за такого способа именования при рефакторинге...
Венгерская номенклатура
Венгерская нотация, изобретенный Чарльзом Симеоне, программистом, работавшим в Xerox PARC с 1972 по 1981 год, за этим предшественникомстал главным дизайнером Microsoft.
Характерной чертой этой номенклатуры является добавлениепрефикс типа,нравится:
- c_name- Имя, тип String (Char)
- n_age- Возраст, число (число) типа
- t_birthday- День рождения, дата/время (время) тип
Но не стоит недооценивать эту номенклатуру, тогда она была очень популярна, и некоторые системы до сих пор используют эту номенклатуру, например Microsoft Win32 API:Более того, эта номенклатура не бесполезна и не имеет определенных преимуществ, по крайней мере, я могу с первого взгляда увидеть тип этого поля.
Просто сегодня это выглядит немного странно, да и не совсем вписывается в нынешний стиль дизайна, а было бы еще интереснее, если бы его поставили на имя человека:
- мужчинаЧжао Си
- ЖенскийСпасибо
- мужчинаЛю Нэн
Когда венгерская номенклатура встречается с JAVA
Наша цель рефакторинга на этот раз — полностью переписать старую системную таблицу, не изменяя ее. Однако новая система разработана на языке Java, а в Java есть стандарт именования с верблюжьим регистром.Что произойдет, когда эта венгерская таблица нотаций будет перенесена в язык Java с верблюжьим регистром?
Например, после того, как имя c_name появится в Java, будет ли оно изменено на CName или cName? Это кажется немного странным, но в итоге мы выбрали CName и полностью сделали префикс типа заглавным, по крайней мере выглядит немного нормально, не так античеловечно
- c_name -> CName
- n_age -> NAge
- t_birthday -> TBirthday
проблема сериализации
Я только определился со способом именования, и прежде чем долго радоваться, столкнулся с очень неудобной проблемой...
Поскольку это корзина семейства Spring, веб-уровень также использует Spring MVC. Библиотека обработки JSON по умолчанию для Spring MVC — Jackson.После того, как веб-уровень вернет JSON, данные будут выглядеть так:
{
"nid":1,
"ctitle":"Effective JAVA"
}
Но мой класс POJO переводит поля венгерской номенклатуры в верхний регистр, это выглядит так:
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;
}
}
Имя поля в верхнем регистре становится строчным после конвертации в JSON. Если это поле в нижнем регистре отдано на фронтенд, то имя фронтенда обязательно будет строчным, и фронтенд тоже должен быть строчным, когда он отправляется на бэкенд.
Это потому, что мы сериализуем параметры в Джексоне и из него.Для Джексона мы можем входить и выходить, когда захотим, и мы все еще можем разобрать его.Это не кажется проблемой, но это немного отвратительно.
Но... не все так просто. Серверная часть не будет использовать все сообщения в качестве полей POJO, будут некоторые динамические поля, которые хранятся в карте. Для этих полей, хранящихся в Map, Джексон не будет преобразовывать в нижний регистр при выводе, но сохранит исходную форму в верхнем регистре, что приведет к полям внешнего интерфейса, хотя все они в венгерском стиле, но некоторые в верхнем регистре, а некоторые в нижнем... Хотя фронтенд не знаю бить людей или нет, но я не смею так играть
Различные библиотеки сериализации имеют разные механизмы обработки.
Обработка венгерской нотации Джексона
На самом деле, причина, по которой Джексон преобразуется в нижний регистр после сериализации, также хорошо объяснена.Спецификация дизайна Java заключается в том, что свойства в bean-компоненте украшаются приватными, а затем предоставляется геттер/сеттер для обеспечения чтения/записи. Затем во время сериализации Джексон получает доступ к значению свойства через метод получения поля и даже использует метод получения для разрешения имени свойства.
Стандарт именования методов получения в Java заключается в преобразовании нижнего регистра верблюда в верхний регистр верблюда Джексон анализирует getNID в nid при анализе имени поля через имя метода получения, поэтому окончательное выходное имя поля — nid нижнего регистра.
Обработка венгерской нотации FastJson
Изначально я хотел настроить процесс именования Джексона, но подумав об этом, я почувствовал, что в этом нет необходимости. Ведь наше именование не стандартное. Почему мы должны менять этот процесс именования?
Поэтому я попытался использовать другую библиотеку JSON, чтобы решить эту проблему с именами.Сначала попробуйте Ali FastJSON, чтобы увидеть, как справляется эта домашняя библиотека:
{
"cTitle":"Effective JAVA",
"nId":1
}
Когда я увидел этот результат сериализации, я чуть не сломал Backspace на своей клавиатуре... Имя свойства также анализируется методом геттера, и правила разбора двух библиотек могут быть разными...
В FastJson c в нижнем регистре, но T в заголовке по-прежнему в верхнем регистре, @#¥%...& 100 слов здесь опущены...
Успокоившись, я несколько раз про себя процитировала: «Я не виню других, это наша собственная проблема с именами. Не ваше дело, как люди это анализируют, если это не соответствует стандартам...»
Тем не менее, экология JAVA настолько хороша, и есть более двух библиотек JSON.Другое — это то, что Gson от Google тоже очень хорош!
Венгерская номенклатура Гсона
Поэтому я снова переключился на Gson, и после настройки Spring MVC Gson Converter результат был таким:
{
"NId":1,
"CTitle":"Effective JAVA"
}
Наконец, я переключился на библиотеку JSON, которая может нормально отображать исходное имя поля, но поскольку она может сохранять исходное имя поля вместо имени свойства, проанализированного в геттере, она не должна анализировать имя метода геттера.
Итак, я снова просмотрел исходный код Gson и обнаружил, что это прямойgetDeclaredFields()
,ПотомmakeAccessible
, и, наконец, непосредственно черезField.getValue
способ получить значение свойства напрямую.
По сравнению с методом получения списка свойств через геттер и последующим вызовом метода геттера для получения значения свойства в Джексоне и FastJson, практика форсирования доступа к приватным свойствам все еще слишком жестока, но мне это нравится, по крайней мере, это может легко решить мою проблему проблема
Другие проблемы с сериализацией
В дополнение к сериализации в форме JSON, некоторые двоичные сериализации также будут иметь эту неловкую проблему.Чтобы получить список свойств/значение свойства, нужно ли проанализировать метод получения или напрямую makeAccessible получить доступ к закрытым свойствам?
Я проверил это, например, в методе сериализации Dubbo по умолчанию (упрощенный Hession2 Dubbo), он все ещеgetDeclaredFields
, а затем получить доступ к частной собственности
Во встроенной сериализации JDK ObjectOutputStream тожеgetDeclaredFields
, а затем получить доступ к частной собственности.
Однако этот метод getDeclaredFields и последующего доступа к значениям приватных свойств также имеет некоторые недостатки. Например, при столкновении с путаницей в коде значения приватных свойств будут все зашифрованы, а общедоступные методы — нет, поэтому при встрече с запутанным кодом этот метод будет перепутан, и метод разбора через метод геттера будет будь без проблем.
Таким образом, нет правильного или неправильного способа получить свойства, все можно сделать, но я думаю, что это должно работать через геттер/сеттер, который соответствует спецификации JAVA.
Пополнить
благодарный@Пользователь 3323102545477Напоминаем, что Джексон очень мощный и поддерживает получение свойств конфигурации, которые можно настроитьVisibility
добиться только путемField
без прохожденияgetter
чтобы получить свойства:
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.NONE)
public class Book {
private Integer NId = 1;
private String CName = "John";
}
Глобальная конфигурация более удобна:
ObjectMapper objectMapper = new ObjectMapper();
// 配置 field 为可见
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
// 配置 getter 不可见
objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
Суммировать
Стандартный способ доступа к значениям приватных свойств в Java — через метод getter, но он по-прежнему предоставляет операцию makeAccessible, которая позволяет нам напрямую обращаться к приватным свойствам или приватным методам.
Я никогда не понимал, почему JDK был разработан таким образом, раз спецификация была указана, зачем открывать бэкдор? Если эту функцию ограничить, то все сериализованные библиотеки можно будет унифицировать, и не будет такой отвратительной проблемы несогласованности!
Но сравнивая три вышеупомянутые библиотеки сериализации, я думаю, что с ними все в порядке.Jackson/FastJson следуют стандартному пути и получают его честно через метод геттера, в то время как Gson немного агрессивен и напрямую обращается к приватным свойствам, каждый со своими преимуществами.
Дополнение: Джексон поддерживает способ получения атрибутов, по умолчанию черезgetter
Получить, но также можно настроить черезField
Получить, см. конфигурацию вышеПополнитьчасть
Нелегко быть оригинальным, и несанкционированная перепечатка запрещена. Если моя статья полезна для вас, пожалуйста, поставьте лайк/добавьте в избранное/подпишитесь, чтобы поддержать и поддержать ее ❤❤❤❤❤❤