资讯专栏INFORMATION COLUMN

Spring Cloud Feign Clients 无需 Controller自动暴露Restfu

GeekGhc / 979人阅读

摘要:前言在开发应用中,作为声明式调用的事实标准极大的简化了远程调用,提供了类本地化的调用方式。本文介绍一种通过动态代理的方式无需直接暴露接口。后语关于这个框架的介绍,后续详细的给大家进行介绍。

前言

在开发SpringCloud应用中,Feign作为声明式调用的事实标准极大的简化了Rest远程调用,提供了类本地化的调用方式。服务提供方的接口暴露方式是通过Controller暴露Restful,而在这个Controller的代码现实中大部分都是处理请求然后再调用Service中的方法,是一个比较模板化的功能,但是工作量确不少。本文介绍一种通过动态代理的方式无需Controller直接暴露Restful接口。
本文中使用笔者在Github开源的框架来实现,本文的讲解也在这个框架基础之上来说明
Git路径:https://github.com/leecho/spr...

依赖
     
        com.github.leecho
        spring-cloud-starter-feign-proxy
        {last-version}
    
实现 定义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 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 list() {
        System.out.println("List execute");
        List 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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72096.html

相关文章

  • springcloud框架的简单搭建(消费服务基于feign

    摘要:不过,我们搭建好框架就是为了消费它使用它,那么这篇文章就来看看如何去消费使用我们之前搭建起来的服务吧首先本文是基于上一篇文章进行的。代码如下启动程序,多次访问,浏览器显示内容为至此我们这个服务消费的框架就搭建完毕了。。。 上一篇文章主要介绍了如何搭建一个简单的springcloud框架。不过,我们搭建好框架就是为了消费它使用它,那么这篇文章就来看看如何去消费使用我们之前搭建起来的服务吧...

    xiguadada 评论0 收藏0
  • 史上最简单的SpringCloud教程 | 第二篇: 服务消费者(rest+ribbon)

    摘要:在服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于的。配置文件如下在工程的启动类中通过向服务中心注册并且注册了一个通过注册表明,这个是负载均衡的。 转载请标明出处: http://blog.csdn.net/forezp/a...本文出自方志朋的博客 在上一篇文章,讲了服务的注册和发现。在服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http re...

    dreamans 评论0 收藏0
  • springCloud学习2(服务发现)

    摘要:服务消费者可以使用多种模型来发现服务。客户端将定期与服务发现层进行通信,并刷新服务实例的缓存。为了达成目的,我们将要学习使用个不同的客户端库,服务消费者可以使用它们来和进行交互。 本篇代码存放于:github 一、服务发现架构   服务发现架构通常具有下面 4 个概念: 服务注册:服务如何使用服务发现代理进行注册? 服务地址的客户端查找:服务客户端查找服务信息的方法是什么? 信息共享...

    史占广 评论0 收藏0
  • SpringCloud学习(2)

    摘要:此为的结构图上篇已注册了,的服务,接下来用,实现负载均衡和的简单客户端,让消费者调用服务。是发布的云中间层服务开源项目,其主要功能是提供客户侧软件负载均衡算法,将的中间层服务连接在一起。对选定的负载均衡策略机上重试机制。           上篇已经搭建好基础demo,接下来继续构建项目并对spring cloud组件介绍描述。 showImg(https://segmentfault...

    wenzi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<