Spring Cloud Feign Clients 无需 Controller自动暴露Restful接口

55次阅读

共计 4836 个字符,预计需要花费 13 分钟才能阅读完成。

前言
在开发 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

正文完
 0