广告
返回顶部
首页 > 资讯 > 精选 >Spring Boot中怎么定义接口
  • 559
分享到

Spring Boot中怎么定义接口

2023-06-15 14:06:52 559人浏览 独家记忆
摘要

Spring Boot中怎么定义接口,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。我们在 Controller 中定义接口的时候,一般都是像下面这样:@GetMapping

Spring Boot中怎么定义接口,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

我们在 Controller 中定义接口的时候,一般都是像下面这样:

@GetMapping("/01")  public String hello(Map<String,Object> map) {      map.put("name", "javaboy");      return "forward:/index";  }

估计很少有人会把接口方法定义成 private 的吧?那我们不禁要问,如果非要定义成 private 的方法,那能运行起来吗?

带着这个疑问,我们开始今天的源码解读~

在我们使用 spring Boot 的时候,经常会看到 HandlerMethod 这个类型,例如我们在定义拦截器的时候,如果拦截目标是一个方法,则 preHandle 的第三个参数就是 HandlerMethod(以下案例选自松哥之前的视频:手把手教你 Spring Boot 自定义注解):

@Component  public class IdempotentInterceptor implements HandlerInterceptor {      @Autowired      TokenService tokenService;      @Override      public boolean preHandle(httpservletRequest request, HttpServletResponse response, Object handler) throws Exception {          if (!(handler instanceof HandlerMethod)) {              return true;          }          //省略...          return true;      }      //...  }

我们在阅读 springMVC 源码的时候,也会反复看到这个 HandlerMethod,那么它到底是什么意思?今天我想和小伙伴们捋一捋这个问题,把这个问题搞清楚了,前面的问题大家也就懂了。

1.概览

Spring Boot中怎么定义接口

可以看到,HandlerMethod 体系下的类并不多:

HandlerMethod

封装 Handler 和具体处理请求的 Method。

InvocableHandlerMethod

在 HandlerMethod 的基础上增加了调用的功能。

ServletInvocableHandlerMethod

在 InvocableHandlerMethod 的基础上增了对 @ResponseStatus 注解的支持、增加了对返回值的处理。

ConcurrentResultHandlerMethod

在 ServletInvocableHandlerMethod 的基础上,增加了对异步结果的处理。

基本上就是这四个,接下来松哥就来详细说一说这四个组件。

2.HandlerMethod

2.1 bridgedMethod

在正式开始介绍 HandlerMethod 之前,想先和大家聊聊 bridgedMethod,因为在 HandlerMethod 中将会涉及到这个东西,而有的小伙伴可能还没听说过 bridgedMethod,因此松哥在这里做一个简单介绍。

首先考考大家,下面这段代码编译会报错吗?

public interface Animal<T> {      void eat(T t);  }  public class Cat implements Animal<String> {      @Override      public void eat(String s) {          System.out.println("cat eat " + s);      }  }  public class Demo01 {      public static void main(String[] args) {          Animal animal = new Cat();          animal.eat(new Object());      } }

首先我们定义了一个 Animal 接口,里边定义了一个 eat 方法,同时声明了一个泛型。Cat 实现了 Animal 接口,将泛型也定义为了 String。当我调用的时候,声明类型是 Animal,实际类型是 Cat,这个时候调 eat 方法传入了 Object 对象大家猜猜会怎么样?如果调用 eat 方法时传入的是 String 类型那就肯定没问题,但如果不是 String 呢?

松哥先说结论:编译没问题,运行报错。

如果小伙伴们在自己电脑上写出上面这段代码,你会发现这样一个问题,开发工具中提示的参数类型竟然是 Object,以松哥的 idea 为例,如下:

Spring Boot中怎么定义接口

大家看到,在我写代码的时候,开发工具会给我提示,这个参数类型是 Object,有的小伙伴会觉得奇怪,明明是泛型,怎么变成 Object 了?

我们可以通过反射查看 Cat 类中到底有哪些方法,代码如下:

public class Demo01 {      public static void main(String[] args) {          Method[] methods = Cat.class.getMethods();          for (Method method : methods) {              String name = method.getName();              Class<?>[] parameterTypes = method.getParameterTypes();              System.out.println(name+"("+ Arrays.toString(parameterTypes) +")");          }      }  }

运行结果如下:

Spring Boot中怎么定义接口

可以看到,在实际运行过程中,竟然有两个 eat 方法,一个的参数为 String 类型,另一个参数为 Object 类型,这是怎么回事呢?

这个参数类型为 Object 的方法其实是 Java 虚拟机在运行时创建出来的,这个方法就是我们所说的 bridge method。本节的小标题叫做 bridgedMethod,这是 HandlerMethod 源码中的变量名,bridge 结尾多了一个 d,含义变成了被 bridge 的方法,也就是参数为 String 的原方法,大家在接下来的源码中看到了 bridgedMethod 就知道这表示参数类型不变的原方法。

2.2 HandlerMethod 介绍

接下来我们来简单看下 HandlerMethod。

在我们前面分析 HandlerMapping 的时候(参见:),里边有涉及到 HandlerMethod,创建 HandlerMethod 的入口方法是 createWithResolvedBean,因此这里我们就从该方法开始看起:

public HandlerMethod createWithResolvedBean() {   Object handler = this.bean;   if (this.bean instanceof String) {    String beanName = (String) this.bean;    handler = this.beanFactory.getBean(beanName);  }   return new HandlerMethod(this, handler);  }

这个方法主要是确认了一下 handler 的类型,如果 handler 是 String 类型,则根据 beanName 从 Spring 容器中重新查找到 handler 对象,然后构建 HandlerMethod:

private HandlerMethod(HandlerMethod handlerMethod, Object handler) {   this.bean = handler;   this.beanFactory = handlerMethod.beanFactory;   this.beanType = handlerMethod.beanType;   this.method = handlerMethod.method;   this.bridgedMethod = handlerMethod.bridgedMethod;   this.parameters = handlerMethod.parameters;   this.responseStatus = handlerMethod.responseStatus;   this.responseStatusReason = handlerMethod.responseStatusReason;   this.resolvedFromHandlerMethod = handlerMethod;   this.description = handlerMethod.description;  }

这里的参数都比较简单,没啥好说的,唯一值得介绍的地方有两个:parameters 和 responseStatus。

parameters

parameters 实际上就是方法参数,对应的类型是 MethodParameter,这个类的源码我这里就不贴出来了,主要和大家说一下封装的内容包括:参数的序号(parameterIndex),参数嵌套级别(nestingLevel),参数类型(parameterType),参数的注解(parameterAnnotations),参数名称查找器(parameterNameDiscoverer),参数名称(parameterName)等。

HandlerMethod 中还提供了两个内部类来封装 MethodParameter,分别是:

  •  HandlerMethodParameter:这个封装方法调用的参数。

  •  ReturnValueMethodParameter:这个继承自 HandlerMethodParameter,它封装了方法的返回值,返回值里边的 parameterIndex 是 -1。

注意,这两者中的 method 都是 bridgedMethod。

responseStatus

这个主要是处理方法的 @ResponseStatus 注解,这个注解用来描述方法的响应状态码,使用方式像下面这样:

@GetMapping("/04")  @ResponseBody  @ResponseStatus(code = HttpStatus.OK)  public void hello4(@SessionAttribute("name") String name) {      System.out.println("name = " + name);  }

从这段代码中大家可以看到,其实 @ResponseStatus 注解灵活性很差,不实用,当我们定义一个接口的时候,很难预知到该接口的响应状态码是 200。

在 handlerMethod 中,在调用其构造方法的时候,都会调用 evaluateResponseStatus 方法处理 @ResponseStatus 注解,如下:

private void evaluateResponseStatus() {   ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);   if (annotation == null) {    annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);   }   if (annotation != null) {    this.responseStatus = annotation.code();    this.responseStatusReason = annotation.reason();  }  }

可以看到,这段代码也比较简单,找到注解,把里边的值解析出来,赋值给相应的变量。

这下小伙伴们应该明白了 HandlerMethod 大概是个怎么回事。

3.InvocableHandlerMethod

看名字就知道,InvocableHandlerMethod 可以调用 HandlerMethod 中的具体方法,也就是 bridgedMethod。我们先来看下 InvocableHandlerMethod 中声明的属性:

private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();  private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();  @Nullable  private WEBDataBinderFactory dataBinderFactory;

主要就是这三个属性:

  •  resolvers:这个不用说,参数解析器,前面的文章中松哥已经和大家聊过这个问题了。

  •  parameterNameDiscoverer:这个用来获取参数名称,在 MethodParameter 中会用到。

  •  dataBinderFactory:这个用来创建 WebDataBinder,在参数解析器中会用到。

具体的请求调用方法是 invokeForRequest,我们一起来看下:

@Nullable  public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,    Object... providedArgs) throws Exception {   Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);   return doInvoke(args);  }  @Nullable  protected Object doInvoke(Object... args) throws Exception {   Method method = getBridgedMethod();   ReflectionUtils.makeAccessible(method);   try {    if (KotlinDetector.isSuspendingFunction(method)) {    return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);    }    return method.invoke(getBean(), args);   }   catch (InvocationTargetException ex) {    // 省略 ...   }  }

首先调用 getMethodArgumentValues 方法按顺序获取到所有参数的值,这些参数值组成一个数组,然后调用 doInvoke 方法执行,在 doInvoke 方法中,首先获取到 bridgedMethod,并设置其可见(意味着我们在 Controller 中定义的接口方法也可以是 private 的),然后直接通过反射调用即可。当我们没看 Springmvc 源码的时候,我们就知道接口方法最终肯定是通过反射调用的,现在,经过层层分析之后,终于在这里找到了反射调用代码。

最后松哥再来说一下负责参数解析的 getMethodArgumentValues 方法:

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,    Object... providedArgs) throws Exception {   MethodParameter[] parameters = getMethodParameters();   if (ObjectUtils.isEmpty(parameters)) {    return EMPTY_ARGS;   }   Object[] args = new Object[parameters.length];   for (int i = 0; i < parameters.length; i++) {    MethodParameter parameter = parameters[i];    parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);    args[i] = findProvidedArgument(parameter, providedArgs);    if (args[i] != null) {     continue;    }    if (!this.resolvers.supportsParameter(parameter)) {     throw new IllegalStateException(fORMatArgumentError(parameter, "No suitable resolver"));    }    try {     args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);    }    catch (Exception ex) {     // 省略...    }   }   return args;  }
  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2.  首先调用 getMethodParameters 方法获取到方法的所有参数。

  3.  创建 args 数组用来保存参数的值。

  4.  接下来一堆初始化配置。

  5.  如果 providedArgs 中提供了参数值,则直接赋值。

  6.  查看是否有参数解析器支持当前参数类型,如果没有,直接抛出异常。

  7.  调用参数解析器对参数进行解析,解析完成后,赋值。

是不是,很 easy!

4.ServletInvocableHandlerMethod

ServletInvocableHandlerMethod 则是在 InvocableHandlerMethod 的基础上,又增加了两个功能:

  • 对 @ResponseStatus 注解的处理

  • 对返回值的处理

Servlet 容器下 Controller 在查找适配器时发起调用的最终就是 ServletInvocableHandlerMethod。

这里的处理核心方法是 invokeAndHandle,如下:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,    Object... providedArgs) throws Exception {   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);   setResponseStatus(webRequest);   if (returnValue == null) {    if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {     disableContentCachingIfNecessary(webRequest);     mavContainer.setRequestHandled(true);     return;    }   }   else if (StringUtils.hasText(getResponseStatusReason())) {    mavContainer.setRequestHandled(true);    return;   }   mavContainer.setRequestHandled(false);   try {    this.returnValueHandlers.handleReturnValue(      returnValue, getReturnValueType(returnValue), mavContainer, webRequest);   }   catch (Exception ex) {    throw ex;   }  }

  1.  首先调用父类的 invokeForRequest 方法对请求进行执行,拿到请求结果。

  2.  调用 setResponseStatus 方法处理 @ResponseStatus 注解,具体的处理逻辑是这样:如果没有添加 @ResponseStatus 注解,则什么都不做;如果添加了该注解,并且 reason 属性不为空,则直接输出错误,否则设置响应状态码。这里需要注意一点,如果响应状态码是 200,就不要设置 reason,否则会按照 error 处理。

  3.  接下来就是对返回值的处理了,returnValueHandlers#handleReturnValue 方法松哥在之前的文章中和大家专门介绍过,这里就不再赘述,传送门:Spring Boot 中如何统一 api 接口响应格式?。

事实上,ServletInvocableHandlerMethod 还有一个子类 ConcurrentResultHandlerMethod,这个支持异步调用结果处理,因为使用场景较少,这里就不做介绍啦。

关于Spring Boot中怎么定义接口问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网精选频道了解更多相关知识。

--结束END--

本文标题: Spring Boot中怎么定义接口

本文链接: https://www.lsjlt.com/news/280568.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Spring Boot中怎么定义接口
    Spring Boot中怎么定义接口,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。我们在 Controller 中定义接口的时候,一般都是像下面这样:@GetMapping...
    99+
    2023-06-15
  • Spring Data JPA怎么自定义Repository接口
    本篇内容主要讲解“Spring Data JPA怎么自定义Repository接口”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring Data J...
    99+
    2023-06-30
  • spring boot怎么写java web和接口
    本篇内容主要讲解“spring boot怎么写java web和接口”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“spring boot怎么写java w...
    99+
    2023-06-26
  • Spring Boot 中自定义异常怎么处理
    这篇文章将为大家详细讲解有关Spring Boot 中自定义异常怎么处理,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在 Spring Boot 项目中 ,异常统一处理,可以使用 Spring 中 @Co...
    99+
    2023-06-02
  • C#中怎么定义接口
    C#中怎么定义接口,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。C#接口定义之声明接口声明接口在语法上和声明抽象类完全相同,例如这里有一个银行账户的接口:public&nb...
    99+
    2023-06-17
  • Spring Boot怎么自定义监控指标
    今天小编给大家分享一下Spring Boot怎么自定义监控指标的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.创...
    99+
    2023-07-05
  • Spring Boot反爬虫中怎么防止接口盗刷
    这篇文章主要介绍“Spring Boot反爬虫中怎么防止接口盗刷”,在日常操作中,相信很多人在Spring Boot反爬虫中怎么防止接口盗刷问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring Boot...
    99+
    2023-06-17
  • 怎么通过接口安全退出Spring Boot
    怎么通过接口安全退出Spring Boot?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1、在pom.xml中引入actuator, security依赖 ...
    99+
    2023-05-31
    springboot spring boo bo
  • 利用spring-boot怎么允许接口跨域
    今天就跟大家聊聊有关利用spring-boot怎么允许接口跨域,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。pom.xml(依赖的jar)// 在spring-boot-starte...
    99+
    2023-05-31
    springboot 跨域
  • Spring Boot怎么实现接口自动幂等
    今天小编给大家分享一下Spring Boot怎么实现接口自动幂等的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我...
    99+
    2022-10-19
  • spring boot动态生成接口怎么实现
    本篇内容主要讲解“spring boot动态生成接口怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“spring boot动态生成接口怎么实现”吧!在某些业务场景中,我...
    99+
    2023-06-21
  • 怎么在java中定义接口
    今天就跟大家聊聊有关怎么在java中定义接口,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。常用的java框架有哪些1.SpringMVC,Spring Web MVC是一种基于Jav...
    99+
    2023-06-14
  • TypeScript怎么定义接口
    这篇文章主要讲解了“TypeScript怎么定义接口”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“TypeScript怎么定义接口”吧!接口的作用:接口,英文:interface,其作用可以...
    99+
    2023-06-20
  • C#接口怎么定义
    这篇文章主要介绍“C#接口怎么定义”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C#接口怎么定义”文章能帮助大家解决问题。C# 接口(Interface)接口定义了所有类继承接口时应遵循的...
    99+
    2023-06-17
  • Spring Boot怎么读取自定义配置文件
    这篇文章给大家分享的是有关Spring Boot怎么读取自定义配置文件的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。@Value首先,会想到使用@Value注解,该注解只能去解析yaml文件中的简单类型,并绑定到...
    99+
    2023-06-15
  • java SPI怎么定义接口
    这篇文章主要介绍了java SPI怎么定义接口的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java SPI怎么定义接口文章都会有所收获,下面我们一起来看看吧。使用说明服务提供者提供接口的具体实现后,在jar包...
    99+
    2023-06-30
  • Spring Boot集成接口管理工具Knife4j怎么用
    这篇文章主要讲解了“Spring Boot集成接口管理工具Knife4j怎么用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring Boot集成接口管理工具Knif...
    99+
    2023-06-30
  • 怎么实现 Spring Boot 接口参数加密解密
    本篇内容主要讲解“怎么实现 Spring Boot 接口参数加密解密”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么实现 Spring Boot 接口参数加密解密”吧!加密解密本身并不是难事,...
    99+
    2023-06-17
  • spring boot中的404错误信息怎么进行自定义
    spring boot中的404错误信息怎么进行自定义?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。返回json@Bean public EmbeddedSer...
    99+
    2023-05-31
    springboot spring boo 400错误
  • Java中的接口与继承怎么定义
    这篇文章主要讲解了“Java中的接口与继承怎么定义”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中的接口与继承怎么定义”吧!假设我有很多个类,分别是Mysql.java、SQLSer...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作