我们平时开发项目的时候,经常会需要远程调用下其他服务提供的接口,于是我们会使用一些HTTP工具类比如Hutool提供的HttpUtil。前不久SpringBoot 3.0发布了,出了一个Http Interface
的新特性,它允许我们使用声明式服务调用的方式来调用远程接口,今天我们就来聊聊它的使用!简介 Http Interface
让你可以像定义Java接口那样定义HTTP服务,而且用法和你平时写Controller中方法完全一致。它会为这些HTTP服务接口自动生成代理实现类,底层是基于Webflux的WebClient实现的。 使用声明式服务调用确实够优雅,下面是一段使用Http Interface
声明的Http服务代码。使用 在SpringBoot 3.0中使用Http Interface
是非常简单的,下面我们就来体验下。依赖集成 首先在项目的pom.xml
中定义好SpringBoot的版本为3.0.0
; <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0</version> <relativePath/> <!-- lookup parent from repository --> </parent>
由于SpringBoot最低要求为Java 17
,我们需要先安装好JDK 17,安装完成后配置项目的SDK版本为Java 17
,JDK下载地址:https://www.oracle.com/cn/java/technologies/downloads/ 由于Http Interface
需要依赖webflux来实现,我们还需添加它的依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
基本使用 下面以调用mall-tiny-swagger
中的接口为例,我们来体验下Http Interface
的基本使用。首先我们准备一个服务来方便远程调用,使用的是之前的mall-tiny-swagger
这个Demo,打开Swagger看下,里面有一个登录接口和需要登录认证的商品品牌CRUD接口,项目地址:https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-swagger 先在application.yml
中配置好mall-tiny-swagger
的服务地址; remote: baseUrl: http://localhost:8088/
再通过@HttpExchange
声明一个Http服务,使用@PostExchange
注解表示进行POST请求; /** * 定义Http接口,用于调用远程的UmsAdmin服务 * Created by macro on 2022/1/19. */ @HttpExchange public interface UmsAdminApi { @PostExchange("admin/login") CommonResult<LoginInfo> login(@RequestParam("username") String username, @RequestParam("password") String password); }
再创建一个远程调用品牌服务的接口,参数注解使用我们平时写Controller方法用的那些即可; /** * 定义Http接口,用于调用远程的PmsBrand服务 * Created by macro on 2022/1/19. */ @HttpExchange public interface PmsBrandApi { @GetExchange("brand/list") CommonResult<CommonPage<PmsBrand>> list(@RequestParam("pageNum") Integer pageNum, @RequestParam("pageSize") Integer pageSize); @GetExchange("brand/{id}") CommonResult<PmsBrand> detail(@PathVariable("id") Long id); @PostExchange("brand/create") CommonResult create(@RequestBody PmsBrand pmsBrand); @PostExchange("brand/update/{id}") CommonResult update(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrand); @GetExchange("brand/delete/{id}") CommonResult delete(@PathVariable("id") Long id); }
为方便后续调用需要登录认证的接口,我创建了TokenHolder
这个类,把token存储到了Session中; /** * 登录token存储(在Session中) * Created by macro on 2022/1/19. */ @Component public class TokenHolder { /** * 添加token */ public void putToken(String token) { RequestAttributes ra = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = ((ServletRequestAttributes) ra).getRequest(); request.getSession().setAttribute("token", token); } /** * 获取token */ public String getToken() { RequestAttributes ra = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = ((ServletRequestAttributes) ra).getRequest(); Object token = request.getSession().getAttribute("token"); if(token!=null){ return (String) token; } return null; } }
创建Java配置,配置好请求用的客户端WebClient及Http服务对象即可,由于品牌服务需要添加认证头才能正常访问,所以使用了过滤器进行统一添加; @Configuration public class HttpInterfaceConfig { @Value("${remote.baseUrl}") private String baseUrl; @Autowired private TokenHolder tokenHolder; @Bean WebClient webClient() { return WebClient.builder() //添加全局默认请求头 .defaultHeader("source", "http-interface") //给请求添加过滤器,添加自定义的认证头 .filter((request, next) -> { ClientRequest filtered = ClientRequest.from(request) .header("Authorization", tokenHolder.getToken()) .build(); return next.exchange(filtered); }) .baseUrl(baseUrl).build(); } @Bean UmsAdminApi umsAdminApi(WebClient client) { HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build(); return factory.createClient(UmsAdminApi.class); } @Bean PmsBrandApi pmsBrandApi(WebClient client) { HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build(); return factory.createClient(PmsBrandApi.class); } }
接下来在Controller中注入Http服务对象,然后进行调用即可; /** * HttpInterface测试 接口 * Created by macro on 2022/1/19. */ @RestController @Api(tags = "HttpInterfaceController") @Tag(name = "HttpInterfaceController", description = "HttpInterface测试接口") @RequestMapping("/remote") public class HttpInterfaceController { @Autowired private UmsAdminApi umsAdminApi; @Autowired private PmsBrandApi pmsBrandApi; @Autowired private TokenHolder tokenHolder; @ApiOperation(value = "调用远程登录接口获取token") @PostMapping(value = "/admin/login") public CommonResult<LoginInfo> login(@RequestParam String username, @RequestParam String password) { CommonResult<LoginInfo> result = umsAdminApi.login(username, password); LoginInfo loginInfo = result.getData(); if (result.getData() != null) { tokenHolder.putToken(loginInfo.getTokenHead() + " " + loginInfo.getToken()); } return result; } @ApiOperation("调用远程接口分页查询品牌列表") @GetMapping(value = "/brand/list") public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1") @ApiParam("页码") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "3") @ApiParam("每页数量") Integer pageSize) { return pmsBrandApi.list(pageNum, pageSize); } @ApiOperation("调用远程接口获取指定id的品牌详情") @GetMapping(value = "/brand/{id}") public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) { return pmsBrandApi.detail(id); } @ApiOperation("调用远程接口添加品牌") @PostMapping(value = "/brand/create") public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) { return pmsBrandApi.create(pmsBrand); } @ApiOperation("调用远程接口更新指定id品牌信息") @PostMapping(value = "/brand/update/{id}") public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrand) { return pmsBrandApi.update(id,pmsBrand); } @ApiOperation("调用远程接口删除指定id的品牌") @GetMapping(value = "/delete/{id}") public CommonResult deleteBrand(@PathVariable("id") Long id) { return pmsBrandApi.delete(id); } }
测试 下面我们通过Postman进行测试,首先调用登录接口获取到远程服务返回的token了; 再调用下需要登录认证的品牌列表接口,发现可以正常访问。 总结 Http Interface
让我们只需定义接口,无需定义方法实现就能进行远程HTTP调用,确实非常方便!但是其实现依赖Webflux的WebClient,在我们使用SpringMVC时会造成一定的麻烦,如果能独立出来就更好了!参考资料 官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html项目源码地址 https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-http-interface 微信8.0将好友放开到了一万,小伙伴可以加我大号了,先到先得,再满就真没了 扫描下方二维码即可加我微信啦,2022,抱团取暖,一起牛逼。
推荐阅读 公司新来一个同事,把优惠券系统设计的炉火纯青! 18.6K Star!这款 JSON 数据可视化工具太优雅了! 别再用 if 校验参数了,太Low!这才是专业的 SpringBoot 参数校验方式! 领导:谁再用 Redis 实现过期订单关闭,立马滚蛋! 从11s到170ms!看看人家的接口优化技巧,那叫一个优雅! 聊聊公司订单系统的架构进化,好的架构是一点点进化来的! 重磅更新!Mall实战教程全面升级,瞬间高大上了! 40K+Star!Mall电商实战项目开源回忆录! 上一篇: 聊聊公司订单系统的架构进化,好的架构是一点点进化来的! 下一篇: 【讲座预告】同济金融俱乐部大讲堂:数字化时代企业财务管理转型