предисловие
После анализаКак реализован единый формат данных Spring Boot?«После статьи я всегда чувствовал необходимость объяснить механизм привязки данных Spring «Механизм привязки данных Spring».
По умолчанию Spring умеет преобразовывать только простые типы данных. Например, если мы отправим данные запроса типа int, String или boolean, они будут автоматически привязаны к соответствующему типу Java. Но в реальных проектах этого недостаточно, потому что нам может понадобиться привязать более сложные типы объектов.
Нам нужно понять механизм привязки данных Spring, чтобы мы могли более гибко выполнять глобальную настройку или пользовательскую настройку, а затем сделать наш RESTful API более кратким и удобочитаемым. В этой статье по-прежнему сначала объясняется реализация с помощью примера кода, а затем анализируется исходный код, чтобы вы поняли, как работает этот механизм, и знаете, почему, Поехали......
Привязка данных Spring
привязка даты
Давайте посмотрим на следующий фрагмент кода
@RestController
@RequestMapping("/bindings/")
@Slf4j
public class BindingController {
@GetMapping("/{date}")
public void getSpecificDateInfo(@PathVariable LocalDateTime date) {
log.info(date.toString());
}
}
Когда мы запрашиваем этот API с Postman
http://localhost:8080/rgyb/bindings/2019-12-10 12:00:00
Выдает исключение преобразования типа данных, как мы и ожидали
пользовательский конвертерStringToLocalDateTimeConverter, чтобы он осозналorg.springframework.core.convert.converter.Converter<S, T>Интерфейс для реализации нашей пользовательской логики преобразования в переопределенном методе преобразования.
public class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String s) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.CHINESE);
return LocalDateTime.parse(s, formatter);
}
}
Зарегистрируйте преобразователь с контекстом:
@Configuration
public class UnifiedReturnConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToLocalDateTimeConverter());
}
}
Перейдите по приведенной выше ссылке, просмотрите консоль и получите соответствующий результат преобразования, как и ожидалось:
c.e.unifiedreturn.api.BindingController : 2019-12-10T12:00
Зная это, например, наши часто используемые типы перечислений также могут использоваться для привязки данных таким образом.
привязка типа enum
Та же рутина, пользовательский конвертер
public class StringToEnumConverter implements Converter<String, Modes> {
@Override
public Modes convert(String s) {
return Modes.valueOf(s);
}
}
Добавьте его в контекст, пожалуйста, попробуйте сами, зная это, нам больше не нужно делать преобразование данных внутри RESTful API, мы добились глобального контроля, и в то же время сделать весь API более понятным и лаконичным
объект привязки
В некоторых случаях мы хотим привязать данные к объектам, тогда можно сразу подумать об использовании@RequestBodyАннотация, которая обычно используется для получения тела запроса POST и преобразования его в соответствующий объект данных.
В реальных бизнес-сценариях, помимо请求体данные в , нам также нужно请求头данные, такие какtokenтокен содержит информацию о текущем вошедшем в систему пользователе. Для каждого запроса RESTful нам необходимо получить данные токена из заголовка для обработки фактического бизнеса. В этом сценарии вышеупомянутыйConverterа также@RequestBodyОчевидно, что не может удовлетворить наши потребности, в настоящее время мы должны перейти на другое решение:HandlerMethodArgumentResolver
Сначала нам нужно настроить аннотациюLoginUser (Вступают в силу во время выполнения и воздействуют на параметры)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface LoginUser {
}
затем настроитьLoginUserArgumentResolver, чтобы он осозналHandlerMethodArgumentResolverинтерфейс
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
//判断参数是否有自定义注解 LoginUser 修饰
return methodParameter.hasParameterAnnotation(LoginUser.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
LoginUserVo loginUserVo = new LoginUserVo();
String token = request.getHeader("token");
if (Strings.isNotBlank(token)){
//通常这里需要编写 token 解析逻辑,并将其放到 LoginUserVo 对象中
//logic
}
//在此为了快速简洁的做演示说明,省略掉解析 token 部分,直接从 header 指定 key 中获取数据
loginUserVo.setId(Long.valueOf(request.getHeader("userId")));
loginUserVo.setName(request.getHeader("userName"));
return loginUserVo;
}
}
все еще будет настроенLoginUserArgumentResolverдобавить в контекст
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginUserArgumentResolver());
}
Напишите API:
@GetMapping("/id")
public void getLoginUserInfo(@LoginUser LoginUserVo loginUserVo) {
log.info(loginUserVo.toString());
}
Через запрос Postman установите соответствующий K-V в шапке, как показано ниже
http://localhost:8080/rgyb/bindings/id
Отправьте запрос, проверьте консоль и получите ожидаемый результат
c.e.unifiedreturn.api.BindingController : LoginUserVo(id=111111, name=rgyb)
Я считаю, что к этому моменту вы поняли основное использование.Далее мы проанализируем исходный код и увидим суть через явление (надеюсь, вы сможете открыть IDE и выполнить шаги для просмотра)
Анализ исходного кода привязки данных Spring
Сначала нам нужно понять наш обычайLoginUserArgumentResolverзагружается в контекст после того, как вы увиделиАнализ принципа преобразования HttpMessageConverterиКак Springboot возвращает унифицированный формат данных JSON?После этого вы, возможно, уже посмотрели, как и при загрузке MessageConverter, вRequestMappingHandlerAdapterВ классе также есть метод добавления ArgumentResolver, который загружает в контекст как встроенный резолвер, так и пользовательский резолвер Код ключа показан следующим образом:
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList();
resolvers.add(new RequestParamMethodArgumentResolver(this.getBeanFactory(), false));
//其他内置 resolver
resolvers.add(new RequestResponseBodyMethodProcessor(this.getMessageConverters(), this.requestResponseBodyAdvice));
...
...
if (this.getCustomArgumentResolvers() != null) {
resolvers.addAll(this.getCustomArgumentResolvers());
}
...
...
return resolvers;
}
существуетАнализ принципа преобразования HttpMessageConverterВ статье есть трассировка стека вызовов. Я снова вставляю ее сюда и отмечаю красным квадратом. На самом деле мы спокойно передали то, что собираемся сказать в этом разделе, когда будем анализировать messageConverter.
Давайте посмотрим на соответствующий класс:
К настоящему времени вы должны понять причину этого.
Далее давайте проверим, что мы используем каждый день@RequestBodyАннотация такая?
Класс, который обрабатывает эту аннотацию,RequestResponseBodyMethodProcessor, проверьте его диаграмму классов и обнаружите, что он все еще реализованHandlerMethodArgumentResolverинтерфейс
Откройте класс, вы увидите код ниже, я отметил ключевые моменты
Общий процесс обработки такой же, за исключением того, что messageConverter вызывается для анализа данных JSON.
Суммировать
Converter и ArgumentResolver, упомянутые в этой статье и часто используемые в Spring MVC.@InitBinderОбщий процесс аннотации одинаков, и каждый может следовать этой идее, чтобы просмотреть конкретную реализацию. Кроме того, когда мы завершаем ежедневную работу по кодированию, мы можем найти некоторые решения из существующих методов обработки Spring, но предпосылка заключается в том, что вы понимаете некоторые из основных процессов вызова Spring.
Наконец, я надеюсь, что мои друзья смогут открыть IDE и проверить соответствующий код, у вас обязательно будут новые открытия, и мы сможем обсудить их вместе. Код этой статьи был загружен, официальная учетная запись ответит «демо», откройте ссылку, чтобы просмотреть содержимое папки «spring-boot-unified-return», и вы также можете просмотреть предыдущую реализацию кода Spring Boot. единый формат возврата.
вопрос души
- Как показано на рисунке выше, при погоне за исходным кодом обнаруживается, что
HandlerMethodArgumentResolverCompositeдаHandlerMethodArgumentResolverОдин из классов реализации , который имеет переменную-член типа Map, обычно мы используем Map, а тип ключа в основном типа String, но о чем вы сразу думаете, когда видите такой ключ в этой Map? На базовых собеседованиях часто задают вопросы об равенствах и хэш-коде.В следующей статье этот класс будет использоваться для анализа и объяснения этой мелочи, которая вас всегда смущает. - Можете ли вы описать весь процесс вызова Spring Boot в целом?
- Сколько распознавателей встроено в Spring? Вы можете отслеживать и отлаживать, чтобы получить
Добро пожаловать, чтобы продолжать обращать внимание на общественный номер: «Сун Гун И Бин».
- Передовая технология Java для обмена галантереей
- Резюме эффективных инструментов | Ответ на «Инструменты»
- Анализ вопроса интервью и ответ
- Сбор технических данных | Ответ на «данные»
Узнайте о стеке технологий Java легко и весело, думая о чтении детективных романов, и постепенно разлагайте технические проблемы на основе принципов упрощения сложных проблем, превращения абстрактных проблем в конкретные и графические, а технология постоянно обновляется, пожалуйста, продолжайте платить внимание...