Springboot2.x使用feign自定義Decoder,Advice

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章