sringcloud2.0学习-11- Feign客户端调用重构

1.Feign客户端调用重构

在上一篇文章中提到,order服务要通过Feign客户端调用member服务的/member/getmember接口,就必须在order服务中定义一个相同的Feign客户端接口,如果有很多个接口,这样的重复代码就很多了,有没有什么办法可以让这个接口定义只写一遍呢?答案是有的。将之前的项目结构改成这样:
sringcloud2.0学习-11- Feign客户端调用重构_第1张图片

  • springcloud2.0-feign-parent
    所有项目的父项目,package类型选pom,存放公共的依赖
  • springcloud2.0-feign-api-services 子项目,同时也是一个聚合项目,即自己又作为父项目,package类型选pom, 该项目下面放所有服务的接口项目
    springcloud2.0-feign-api-memberservice 定义member服务中的所有接口
    springcloud2.0-feign-api-orderservice 定义order服务中的所有接口
  • springcloud2.0-feign-api-memberservice-impl member服务
  • springcloud2.0-feign-api-orderservice-impl order服务
    这时order服务要调用member服务,只需要依赖springcloud2.0-feign-api-memberservice项目就可以了。
    此外,还需要一个common工程,创建出来的项目结构如下:
    sringcloud2.0学习-11- Feign客户端调用重构_第2张图片

各个工程依赖如下:

  • 聚合项目springcloud2.0-feign-parent,有三个子模块
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<groupId>com.lchtestgroupId>
	<artifactId>springcloud2.0-feign-parentartifactId>
	<version>0.0.1-SNAPSHOTversion>
	<packaging>pompackaging>

	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>2.0.3.RELEASEversion>
	parent>
	
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloudgroupId>
				<artifactId>spring-cloud-dependenciesartifactId>
				<version>Finchley.RELEASEversion>
				<type>pomtype>
				<scope>importscope>
			dependency>
		dependencies>
	dependencyManagement>
	<dependencies>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-openfeignartifactId>
		dependency>
		<dependency>
			<groupId>org.projectlombokgroupId>
			<artifactId>lombokartifactId>
		dependency>
	dependencies>
	<modules>
		<module>springcloud2.0-feign-api-servicesmodule>
		<module>springcloud2.0-feign-api-memberservice-implmodule>
		<module>springcloud2.0-feign-api-orderservice-implmodule>
	modules>
project>
  • 子模块springcloud2.0-feign-api-services(也定义为一个聚合项目)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>
  <parent>
    <groupId>com.lchtestgroupId>
    <artifactId>springcloud2.0-feign-parentartifactId>
    <version>0.0.1-SNAPSHOTversion>
  parent>
  <artifactId>springcloud2.0-feign-api-servicesartifactId>
  <packaging>pompackaging>
  <modules>
  	<module>springcloud2.0-feign-api-memberservicemodule>
  	<module>springcloud2.0-feign-api-orderservicemodule>
  modules>
project>
  • 子模块springcloud2.0-feign-api-memberservice
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>
  <parent>
    <groupId>com.lchtestgroupId>
    <artifactId>springcloud2.0-feign-api-servicesartifactId>
    <version>0.0.1-SNAPSHOTversion>
  parent>
  <artifactId>springcloud2.0-feign-api-memberserviceartifactId>
project>
  • 子模块springcloud2.0-feign-api-orderservice
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>
  <parent>
    <groupId>com.lchtestgroupId>
    <artifactId>springcloud2.0-feign-api-servicesartifactId>
    <version>0.0.1-SNAPSHOTversion>
  parent>
  <artifactId>springcloud2.0-feign-api-orderserviceartifactId>
project>
  • 子模块springcloud2.0-feign-api-memberservice-impl
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>
  <parent>
    <groupId>com.lchtestgroupId>
    <artifactId>springcloud2.0-feign-parentartifactId>
    <version>0.0.1-SNAPSHOTversion>
  parent>
  <artifactId>springcloud2.0-feign-api-memberservice-implartifactId>
project>
  • 子模块springcloud2.0-feign-api-orderservice-impl
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>
  <parent>
    <groupId>com.lchtestgroupId>
    <artifactId>springcloud2.0-feign-parentartifactId>
    <version>0.0.1-SNAPSHOTversion>
  parent>
  <artifactId>springcloud2.0-feign-api-orderservice-implartifactId>
project>

注:eureka注册 中心使用单注册中心,将其他eureka server项目配置改一下即可。

2. 服务提供者

2.1 服务提供者接口定义

sringcloud2.0学习-11- Feign客户端调用重构_第3张图片

package com.lchtest.api.service;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.lchtest.api.entity.UserEntity;
import com.lchtest.common.base.ResponseBase;

/**
 * memberservice接口定义
 * @author pc
 *
 */
public interface IMemberService {

	// 实体类放在接口项目里面比较好,可能其他项目也会依赖该实体类,代码实现放到接口的实现里面
	@RequestMapping("/getMember")
	public UserEntity getMember(@RequestParam("name") String name);
	
	@RequestMapping("/getUserInfo")
	public ResponseBase getUserInfo();
}

上面使用@RequestParam接收请求参数(order服务调用member服务时带请求参数); order服务要调用member服务的/getMember接口,只需要引入springcloud2.0-feign-api-memberservice的依赖就可以了,代码都是一样的

package com.lchtest.api.entity;

import lombok.Data;

@Data
public class UserEntity {
	private String name;
	private int age;
}
2.2 服务提供者接口实现

sringcloud2.0学习-11- Feign客户端调用重构_第4张图片
pom引入springcloud2.0-feign-api-memberservice 依赖

package com.lchtest.api.service.impl;

import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.lchtest.api.entity.UserEntity;
import com.lchtest.api.service.IMemberService;
import com.lchtest.common.base.BaseApiService;
import com.lchtest.common.base.ResponseBase;
/**
 * memberService的业务实现
 * @author pc
 *
 */
@RestController
public class MemberServiceImpl extends BaseApiService implements IMemberService {

	
	@Value("${server.port}")
	private String serverPort;

	@GetMapping("/")
	public String index(HttpServletRequest req) {
		System.out.println("我是首页.....");
		return "我是member服务" + serverPort;
	}
	
	@RequestMapping("/getMember")
	@Override
	public UserEntity getMember(String name) {
		UserEntity userEntity = new UserEntity();
		userEntity.setName(name);
		return userEntity;
	}
}

实现类里面定义的接口也要加上@RequestMapping("/getMember")注解!

配置文件:

#服务启动端口号
server:
  port: 8001
#服务名称(服务注册到eureka名称)  
spring:
    application:
        name: app-member
#服务注册到eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8100/eureka        
    #把自己注册到注册中心
    register-with-eureka: true
    # 从eureka上获取注册信息
    fetch-registry: true

启动类:加上注解@EurekaClient @EnableFeignClients

package com.lchtest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class AppMember {
	public static void main(String[] args) {
		SpringApplication.run(AppMember.class, args);
	}
}

启动eureka注册中心和member服务并访问,说明member服务ok
在这里插入图片描述

3.服务消费者

sringcloud2.0学习-11- Feign客户端调用重构_第5张图片

3.1服务消费者接口定义
package com.lchtest.api.service;

import org.springframework.web.bind.annotation.RequestMapping;
import com.lchtest.common.base.ResponseBase;

public interface IOrderService {
	// 订单服务调用会员服务接口
	@RequestMapping("/orderToMember")
	public String orderToMember(String name);

	// 订单服务调用会员服务接口
	@RequestMapping("/orderToMemberUserInfo")
	public ResponseBase orderToUserInfo();
	
	@RequestMapping("/orderInfo")
	public ResponseBase orderInfo();
}
3.2 服务消费者接口实现

sringcloud2.0学习-11- Feign客户端调用重构_第6张图片
pom:
引入springcloud2.0-feign-api-orderservice,实现该项目定义的接口;
引入springcloud2.0-feign-api-memberservice,目的是为了调用member服务的接口

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>
  <parent>
    <groupId>com.lchtestgroupId>
    <artifactId>springcloud2.0-feign-parentartifactId>
    <version>0.0.1-SNAPSHOTversion>
  parent>
  <artifactId>springcloud2.0-feign-api-orderservice-implartifactId>
 
	<dependencies>
	    
		<dependency>
			<groupId>com.lchtestgroupId>
			<artifactId>springcloud2.0-feign-api-orderserviceartifactId>
			<version>0.0.1-SNAPSHOTversion>
		dependency>
		
		
		<dependency>
			<groupId>com.lchtestgroupId>
			<artifactId>springcloud2.0-feign-api-memberserviceartifactId>
			<version>0.0.1-SNAPSHOTversion>
		dependency>
		
		<dependency>
			<groupId>com.lchtestgroupId>
			 <artifactId>springcloud2.0-feign-commonartifactId>
			 <version>0.0.1-SNAPSHOTversion>
		dependency>
	dependencies>
project>

3.2.1 Feign客户端
原来的写法是,member服务中会有这么一个接口声明,order服务要调用 member服务的对应接口,也得把这两行代码重复写一遍

 @RequestMapping("/getMember")
 public UserEntity getMember(String name);

项目结构重构之后,Feign客户端接口不用再写重复代码,只需要继承IMemberService,类名上面加上@FeignClient(name = “app-member”)注解,就可以使用Feign客户端调用member服务的 /getMember 接口了!
由于orderservice调用memberService的getMember接口时带了请求参数,因此IMemberService的getMember方法要使用@RequestParam注解接收请求参数,如下:

package com.lchtest.api.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.lchtest.api.entity.UserEntity;
import com.lchtest.api.fallback.MemberServiceFallback;
import com.lchtest.api.service.IMemberService;

/**
 * Feign客户端定义,继承IMemberService,这样就可以避免类似下面这样的重复代码了: @RequestMapping("/getMember")
 * public UserEntity getMember(String name);
 */
@FeignClient(name = "app-member")
public interface MemberServiceFeign extends IMemberService {

}

Tips: Feign能否写在会员服务的接口IMemberService 里面,答案是否定的,
因为Feign客户端一般需要做服务降级,@FeignClient(name = “app-member”, fallback = MemberServiceFallback.class) 注解中通过fallback 指定服务降级要调用的类,而服务降级只能在接口的调用端完成!

orderservice接口的实现:需要自动注入 MemberServiceFeign

package com.lchtest.api.service.impl;

import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.lchtest.api.entity.UserEntity;
import com.lchtest.api.fallback.MemberServiceFallback;
import com.lchtest.api.feign.MemberServiceFeign;
import com.lchtest.api.service.IOrderService;
import com.lchtest.common.base.BaseApiService;
import com.lchtest.common.base.ResponseBase;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

/**
 * 订单服务继承会员服务接口,用来实现Feign客户端,减少重复接口代码!
 * 
 * @author pc
 *
 */
@RestController
public class OrderServiceImpl extends BaseApiService implements IOrderService {

	@Autowired
	private MemberServiceFeign memberServiceFeign;
	
	@Value("${server.port}")
	private String serverPort;

	@GetMapping("/")
	public String index(HttpServletRequest req) {
		System.out.println("我是首页.....");
		return "我是order服务" + serverPort;
	}

	/**
	 * http://localhost:8005/orderToMember?name=admin
	 */
	@RequestMapping("/orderToMember")
	@Override
	public String orderToMember(String name) {
		UserEntity user = memberServiceFeign.getMember(name);
		return user == null ? "no user find" : user.toString();
	}
}

启动类:

package com.lchtest.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients //开启Feign客户端
public class AppOrder {
	public static void main(String[] args) {
		SpringApplication.run(AppOrder.class, args);
	}
}

启动注册中心,member服务,order服务,测试ok:
sringcloud2.0学习-11- Feign客户端调用重构_第7张图片
上面所有代码,漏了一个common工程,用来封装接口的响应,如下:

  • pom
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>
  <parent>
    <groupId>com.lchtestgroupId>
    <artifactId>springcloud2.0-feign-parentartifactId>
    <version>0.0.1-SNAPSHOTversion>
  parent>
  <artifactId>springcloud2.0-feign-commonartifactId>
project>
package com.lchtest.common.base;

public class BaseApiService {

	public ResponseBase setResultError(Integer code, String msg) {
		return setResult(code, msg, null);
	}

	// 返回错误,可以传msg
	public ResponseBase setResultError(String msg) {
		return setResult(500, msg, null);
	}

	// 返回成功,可以传data值
	public ResponseBase setResultSuccess(Object data) {
		return setResult(200, "处理成功", data);
	}

	// 返回成功,沒有data值
	public ResponseBase setResultSuccess() {
		return setResult(200, "处理成功", null);
	}

	// 返回成功,沒有data值
	public ResponseBase setResultSuccess(String msg) {
		return setResult(200, msg, null);
	}

	// 通用封装
	public ResponseBase setResult(Integer code, String msg, Object data) {
		return new ResponseBase(code, msg, data);
	}
}
package com.lchtest.common.base;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

//服务接口有响应  统一规范响应服务接口信息
@Data
@Slf4j
public class ResponseBase {
	private Integer rtnCode;
	private String msg;
	private Object data;

	public ResponseBase() {

	}

	public ResponseBase(Integer rtnCode, String msg, Object data) {
		super();
		this.rtnCode = rtnCode;
		this.msg = msg;
		this.data = data;
	}

	@Override
	public String toString() {
		return "ResponseBase [rtnCode=" + rtnCode + ", msg=" + msg + ", data=" + data + "]";
	}
}

你可能感兴趣的:(SpringCloud2.0,spring,spring,boot,java)