1.首先實現服務提供項目********
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.frank</groupId>
<artifactId>feign-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>feign-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
server.port=8081
spring.application.name=user-service
server.servlet.context-path=/api
Grade
package com.frank.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author 小石潭記
* @date 2020/6/25 12:02
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Grade {
private int id;
private String name;
private List<User> users;
private String address;
}
User
package com.frank.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 小石潭記
* @date 2020/6/22 21:42
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
}
ServerController
package com.frank.web;
import com.frank.entity.Grade;
import com.frank.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
/**
* @author 小石潭記
* @date 2020/6/22 21:40
* @Description: ${todo}
*/
@RestController
@Slf4j
public class ServerController {
@GetMapping("/getUserInfo")
public Map<String,Object> getUserInfo() {
Map<String,Object> map = new HashMap<>();
map.put("code", 200);
map.put("msg", "success");
map.put("data", new User(1,"小石潭記",26));
log.info("返回成功數據成功:{},{}", map, new Date());
return map;
}
@GetMapping("/getGradeInfo")
public Map<String,Object> getGradeInfo() {
Map<String,Object> map = new HashMap<>();
List<User> users = new ArrayList<>();
users.add(new User(1,"小石潭記",8));
users.add(new User(2,"陋室銘",7));
users.add(new User(3,"桃花源記",9));
Grade grade = new Grade(1,"二年級",users, "重慶市忠縣");
map.put("code", 200);
map.put("msg", "success");
map.put("data", grade);
log.info("返回geGradeInfo數據成功:{},{}", map, new Date());
return map;
}
@GetMapping("/getFailInfo")
public Map<String,Object> getFailInfo() {
Map<String,Object> map = new HashMap<>();
map.put("code", 404);
map.put("msg", "fail");
map.put("data", null);
log.info("返回失敗數據成功:{},{}", map, new Date());
return map;
}
}
FeignServerApplication
package com.frank;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FeignServerApplication {
public static void main(String[] args) {
SpringApplication.run(FeignServerApplication.class, args);
}
}
服務提供已完成。。。
2.feign-client項目******
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.frank</groupId>
<artifactId>feign-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>feign-client</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
server.port=8082
spring.application.name=feign-client
server.servlet.context-path=/client
# 開啓熔斷
#feign.hystrix.enabled=true
MyAdvice
package com.frank.advice;
import com.frank.domain.MyException;
import com.frank.domain.dto.BaseResponse;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* @author 小石潭記
* @date 2020/6/22 21:07
* @Description: ${todo}
*/
@RestControllerAdvice
public class MyAdvice implements ResponseBodyAdvice<Object> {
/**
* 拋出該自定義異常就會執行該方法
*/
@ExceptionHandler(value = MyException.class)
BaseResponse handleMyException(MyException e, HttpServletRequest request){
return BaseResponse.fail(e.getMessage());
}
/**
* 判斷哪些需要攔截 return true 就會執行beforeBodyWrite
*/
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
/**
* 經過decoder之後,就會到達這裏,返回結果前,可以對結果進行處理
*/
@Override
public Object beforeBodyWrite(Object obj, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
System.out.println("==>beforeBodyWrite:" + obj.toString() + ","
+ methodParameter);
return obj;
}
}
MyFeignClient
package com.frank.client;
import com.frank.domain.dto.BaseResponse;
import com.frank.domain.dto.Grade;
import com.frank.domain.dto.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author 小石潭記
* @date 2020/6/22 21:48
* @Description: ${todo}
*/
@FeignClient(
//服務名
name = "user-service",
//服務地址
url = "http://localhost:8081/api"
// ,fallback = UserHihystric.class 暫不開啓熔斷
)
public interface MyFeignClient {
//對應的服務裏的接口地址,及請求方式
@RequestMapping(value = "/getUserInfo", method = RequestMethod.GET)
@ResponseBody
BaseResponse getUserInfo();
@RequestMapping(value = "/getGradeInfo", method = RequestMethod.GET)
@ResponseBody
BaseResponse getGradeInfo();
@RequestMapping(value = "/getFailInfo", method = RequestMethod.GET)
@ResponseBody
BaseResponse getFailInfo();
}
MyDecoder
package com.frank.decoder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.frank.domain.EnumError;
import com.frank.domain.MyException;
import com.frank.domain.dto.BaseResponse;
import feign.FeignException;
import feign.Response;
import feign.Util;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.lang.reflect.Type;
/**
* @author 小石潭記
* @date 2020/6/23 21:09
* @Description: 自定義feign的正常返回結果
*/
@Component
public class MyDecoder implements Decoder {
@Autowired
private ObjectMapper mapper;
/**
* 這裏統一處理,根據狀態碼判斷返回正常還是異常的,
* 200返回正常的,其他狀態碼直接拋出異常
*/
@Override
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
if (response.status() >= 200 && response.status() <= 299) {
String result = Util.toString(response.body().asReader());
BaseResponse baseResponse = mapper.readValue(result, BaseResponse.class);
int code = baseResponse.getCode();
if (code == 200){
return baseResponse;
}
throw new MyException(EnumError.BAD_REQUEST);
}
throw new MyException(EnumError.OTHER_ERROR);
}
}
MyErrorDecoder
package com.frank.decoder;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.frank.domain.EnumError;
import com.frank.domain.ExceptionInfo;
import com.frank.domain.MyException;
import feign.FeignException;
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
* @author 小石潭記
* @date 2020/6/23 20:53
* @Description: 自定義feign錯誤異常處理
*/
@Configuration
public class MyErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
/*if (response.status() >= 400 && response.status() <= 499) {
return new MyException(EnumError.BAD_REQUEST);
}
if (response.status() >= 500 && response.status() <= 599) {
return new MyException(EnumError.SERVER_ERROR);
}
return new MyException(EnumError.OTHER_ERROR);*/
try {
if (response.body() != null) {
ObjectMapper mapper = new ObjectMapper();
ExceptionInfo exceptionInfo =mapper.readValue(Util.toString(response.body().asReader()),
new TypeReference<ExceptionInfo>() {});
Class clazz = Class.forName(exceptionInfo.getException());
return (Exception) clazz.getDeclaredConstructor(String.class).newInstance(exceptionInfo.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
return FeignException.errorStatus(methodKey, response);
}
}
BaseResponse
package com.frank.domain.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* @author 小石潭記
* @date 2020/6/25 12:19
* @Description: ${todo}
*/
@Data
public class BaseResponse<T> {
/**
* 返回的狀態碼
*/
@JsonProperty("code")
private int code;
/**
* 返回的信息
*/
@JsonProperty("msg")
private String message;
/**
* 返回的數據
*/
@JsonProperty("data")
private T data;
public static BaseResponse success(Object object){
BaseResponse baseResponse = new BaseResponse();
baseResponse.setCode(200);
baseResponse.setMessage("success");
baseResponse.setData(object);
return baseResponse;
}
public static BaseResponse fail(Object object){
BaseResponse baseResponse = new BaseResponse();
baseResponse.setCode(404);
baseResponse.setMessage("fail");
baseResponse.setData(object);
return baseResponse;
}
}
Grade
package com.frank.domain.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.frank.domain.dto.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author 小石潭記
* @date 2020/6/25 12:02
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Grade {
@JsonProperty("id")
private int id;
@JsonProperty("name")
private String name;
@JsonProperty("users")
private List<User> users;
@JsonProperty("address")
private String address;
}
User
package com.frank.domain.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 小石潭記
* @date 2020/6/22 21:42
* @Description: ${todo}
*/
@Data
public class User {
@JsonProperty("id")
private int id;
@JsonProperty("name")
private String name;
@JsonProperty("age")
private int age;
}
EnumError
package com.frank.domain;
/**
* @author 小石潭記
* @date 2020/6/23 21:01
* @Description: ${todo}
*/
public enum EnumError {
SERVER_ERROR(500, "服務器異常,請聯繫系統運維人員"),
BAD_REQUEST(400, "請求異常"),
OTHER_ERROR(404, "系統異常");
private int status;
private String reason;
EnumError(int status, String reason){
this.status = status;
this.reason = reason;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}}
ExceptionInfo
package com.frank.domain;
import lombok.Data;
/**
* @author 小石潭記
* @date 2020/6/25 21:10
* @Description: ${todo}
*/
@Data
public class ExceptionInfo{
private Long timestamp;
private Integer status;
//異常包結構-"com.crazy.cloud.common.exception.DataConflictException"
private String exception;
//信息--手機已註冊
private String message;
private String path;
private String error;
}
MyException
package com.frank.domain;
/**
* @author 小石潭記
* @date 2020/6/25 14:09
* @Description: ${todo}
*/
public class MyException extends RuntimeException{
public MyException(EnumError enumError){
super(enumError.getReason());
}
}
MyHihystric
package com.frank.hihystric;
import com.frank.client.MyFeignClient;
import com.frank.domain.dto.BaseResponse;
import com.frank.domain.dto.Grade;
import com.frank.domain.dto.User;
/**
* @author 小石潭記
* @date 2020/6/23 20:31
* @Description: ${todo}
*/
//@Component 暫不開啓熔斷, 全部返回的null,如果需要使用熔斷,返回默認的一個對象即可
public class MyHihystric implements MyFeignClient {
@Override
public BaseResponse<User> getUserInfo() {
return null;
}
@Override
public BaseResponse<Grade> getGradeInfo() {
return null;
}
@Override
public BaseResponse getFailInfo() {
return null;
}
}
ClientController
package com.frank.web;
import com.frank.client.MyFeignClient;
import com.frank.domain.dto.BaseResponse;
import com.frank.domain.dto.Grade;
import com.frank.domain.dto.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* @author 小石潭記
* @date 2020/6/20 21:38
* @Description: ${todo}
*/
@RestController
@Slf4j
public class ClientController {
@Autowired
private MyFeignClient userFeignClient;
@GetMapping("/getUserInfo")
public BaseResponse<User> getUserInfo() {
BaseResponse<User> successInfo = userFeignClient.getUserInfo();
log.info("client->getSuccessInfo返回數據:{},{}", successInfo, new Date());
return successInfo;
}
@GetMapping("/getGradeInfo")
public BaseResponse<Grade> getGradeInfo() {
BaseResponse<Grade> successInfo = userFeignClient.getGradeInfo();
log.info("client->getGradeInfo返回數據:{},{}", successInfo, new Date());
return successInfo;
}
@GetMapping("/getFailInfo")
public BaseResponse getFailInfo() {
BaseResponse failInfo = userFeignClient.getFailInfo();
log.info("client返回失敗數據:{},{}", failInfo, new Date());
return failInfo;
}
}
FeignClientApplication
package com.frank;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
//@EnableHystrixDashboard 暫時不開啓熔斷
public class FeignClientApplication {
public static void main(String[] args) {
SpringApplication.run(FeignClientApplication.class, args);
}
}
啓動項目測試:
http://localhost:8082/client/getUserInfo http://localhost:8082/client/getGradeInfo http://localhost:8082/client/getFailInfo