Spring Cloud Feign Clients 无需 Controller自动暴露Restful接口
前言
在开发SpringCloud应用中,Feign作为声明式调用的事实标准极大的简化了Rest远程调用,提供了类本地化的调用方式。服务提供方的接口暴露方式是通过Controller暴露Restful,而在这个Controller的代码现实中大部分都是处理请求然后再调用Service中的方法,是一个比较模板化的功能,但是工作量确不少。本文介绍一种通过动态代理的方式无需Controller直接暴露Restful接口。
本文中使用笔者在Github开源的框架来实现,本文的讲解也在这个框架基础之上来说明
Git路径:https://github.com/leecho/spr...
依赖
<dependency> <groupId>com.github.leecho</groupId> <artifactId>spring-cloud-starter-feign-proxy</artifactId> <version>{last-version}</version> </dependency>
实现
定义Service Interface
首先定义服务接口,使用@FeignClient标示是一个Feign接口,在这个示例Sevice中定义了CURD和文件上传方法,并使用了一些常用参数注解
@Api(tags = "DemoService", description = "Demo Feign Client") @FeignClient("demo-service") public interface DemoService { /** * create demo * * @param demo * @return */ @ApiOperation(value = "Create demo") @PostMapping(value = "/demo") Demo create(@RequestBody @Valid @ApiParam Demo demo); /** * update demo * * @param demo * @return */ @PutMapping(value = "/demo") Demo update(@RequestBody @Valid Demo demo); /** * delete demo * * @param id * @return */ @DeleteMapping(value = "/demo/{id}") Demo delete(@PathVariable(name = "id") String id); /** * list demo * * @param id * @param headerValue test header value * @return */ @GetMapping(value = "/demo/{id}") Demo get(@PathVariable(name = "id") String id, @RequestHeader(name = "header") String headerValue); /** * list demos * * @return */ @GetMapping(value = "/demos") List<Demo> list(); /** * upload file * * @param file * @return */ @PostMapping(value = "/demo/upload") String upload(@RequestPart(name = "file") MultipartFile file); }
实现Service
在实现类中简单的实现了CURD和上传文件的方法
@Slf4j @Primary @Service public class DemoServiceImpl implements DemoService { @Override public Demo create(Demo demo) { log.info("Create executed : " + demo); return demo; } @Override public Demo update(Demo demo) { log.info("Update execute :" + demo); return demo; } @Override public Demo delete(String id) { log.info("Delete execute : " + id); return Demo.builder().name("demo-" + id).data("data-" + id).build(); } @Override public Demo get(String id, String header) { Demo demo = Demo.builder() .name(header) .data(header).build(); System.out.println("Get execute : " + id + "," + header); return demo; } @Override public List<Demo> list() { System.out.println("List execute"); List<Demo> demos = new ArrayList<>(); for (int i = 0; i < 5; i++) { Demo demo = Demo.builder() .name("demo-" + i) .data("data" + i).build(); demos.add(demo); } return demos; } @Override public String upload(MultipartFile file) { return file.getOriginalFilename(); } }
动态生成Restful接口的原理是动态生成Controller类实现ServiceInterface,如果真正的实现类会被其他BEAN依赖,需要通过注解@Primary来方式来防止依赖冲突
配置应用
在应用中通过@EnableFeignProxies来标示应用在启动的过程中会扫描所有的FeignClients并暴露Restful接口
@EnableFeignProxies(basePackages = "com.github.leecho") @ComponentScan("com.github.leecho") @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
测试用例
在测试用例中对Service的接口进行测试
@SpringBootTest @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration public class DemoApplicationTest { @Autowired private WebApplicationContext context; private MockMvc mvc; @Before public void setUp() { mvc = MockMvcBuilders.webAppContextSetup(context).build(); } @Test public void create() throws Exception { mvc.perform(MockMvcRequestBuilders.post("/demo") .contentType(MediaType.APPLICATION_JSON_UTF8) .content(new ObjectMapper().writeValueAsString(Demo.builder().name("create").data("data").build())) .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } @Test public void update() throws Exception { mvc.perform(MockMvcRequestBuilders.put("/demo") .contentType(MediaType.APPLICATION_JSON_UTF8) .content(new ObjectMapper().writeValueAsString(Demo.builder().name("update").data("data").build())) .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } @Test public void delete() throws Exception { mvc.perform(MockMvcRequestBuilders.delete("/demo/{id}", "1") .contentType(MediaType.APPLICATION_JSON_UTF8) .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } @Test public void get() throws Exception { mvc.perform(MockMvcRequestBuilders.get("/demo/{id}", "1") .contentType(MediaType.APPLICATION_JSON_UTF8) .header("header", "header-value") .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } @Test public void list() throws Exception { mvc.perform(MockMvcRequestBuilders.get("/demos") .contentType(MediaType.APPLICATION_JSON_UTF8) .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } @Test public void upload() throws Exception { mvc.perform(MockMvcRequestBuilders.multipart("/demo/upload").file(new MockMultipartFile("file", "test.txt", ",multipart/form-data", "upload test".getBytes())) .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } }
在启动日志中也可以看到Restful接口已经暴露成功。
后语
关于这个框架的介绍,后续详细的给大家进行介绍。文中所涉及到的代码也在Git中:spring-cloud-feign-proxy-sample
相关推荐
Eiceblue 2020-08-02
ahnjwj 2020-07-28
playis 2020-06-28
playis 2020-06-16
ahnjwj 2020-06-12
84560296 2020-06-10
84560296 2020-06-09
84560296 2020-06-08
84560296 2020-05-30
81901836 2020-05-26
beibeijia 2020-05-16
85291545 2020-05-01
84560296 2020-04-10
fanix 2020-04-09
bapinggaitianli 2020-04-07
84560296 2020-03-27
85291545 2020-03-26
82911731 2020-03-25