SpringCloud系列--4.SpringCloud使用Feign

  |   0 评论   |   0 浏览

搭建EurekaServer

<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.0</modelVersion>
	<groupId>org.crazyit.test.cloud</groupId>
	<artifactId>spring-feign-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Dalston.SR1</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka-server</artifactId>
		</dependency>
	</dependencies>
	
	
</project>
server:
  port: 8761
eureka:
  client:
    registerWithEureka: false
    fetchRegistry: false
package org.crazyit.cloud;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class ServerApplication {

	public static void main(String[] args) {
		new SpringApplicationBuilder(ServerApplication.class).run(args);
	}
}

搭建服务端

<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.0</modelVersion>
	<groupId>org.crazyit.test.cloud</groupId>
	<artifactId>spring-feign-provider</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Dalston.SR1</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
	</dependencies>
	
</project>
spring:
  application:
    name: spring-feign-provider
eureka:
  instance:
    hostname: localhost
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
package org.crazyit.cloud;

public class Person {

	private Integer id;
	
	private String name;
	
	private Integer age;
	
	private String message;

	public Person() {
		super();
	}

	public Person(Integer id, String name, Integer age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
	
}

package org.crazyit.cloud;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FirstController {

	@RequestMapping(value = "/person/{personId}", method = RequestMethod.GET)
	public Person findPerson(@PathVariable("personId") Integer personId, HttpServletRequest request) {
		Person person = new Person(personId, "Crazyit", 30);
		// 为了查看结果,将请求的URL设置到Person实例中
		person.setMessage(request.getRequestURL().toString());
		return person;
	}
	
	@RequestMapping(value = "/hello", method = RequestMethod.GET)
	@ResponseBody
	public String hello() {
		return "Hello World";
	}
}

package org.crazyit.cloud;

import java.util.Scanner;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class ProviderApplication {

	public static void main(String[] args) {
		// 读取控制台输入的端口,避免端口冲突
		Scanner scan = new Scanner(System.in);
		String port = scan.nextLine();
		new SpringApplicationBuilder(ProviderApplication.class).properties(
				"server.port=" + port).run(args);
	}
}

搭建调用者

<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.0</modelVersion>
	<groupId>org.crazyit.test.cloud</groupId>
	<artifactId>spring-feign-invoker</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Dalston.SR1</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-ribbon</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-feign</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.16.18</version>
		</dependency>
	</dependencies>

</project>

启用Feign @EnableFeignClients

package org.crazyit.cloud;

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

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class InvokerApplication {

	public static void main(String[] args) {
		SpringApplication.run(InvokerApplication.class, args);
	}
}

定义@FeignClient("spring-feign-provider")

package org.crazyit.cloud;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


@FeignClient("spring-feign-provider") //声明调用的服务名称
public interface PersonClient {
	
	@RequestMapping(method = RequestMethod.GET, value = "/hello")
	String hello();

	@RequestMapping(method = RequestMethod.GET, value = "/person/{personId}")
	Person getPerson(@PathVariable("personId") Integer personId);
	

}

Controller类

package org.crazyit.cloud;

import org.crazyit.cloud.contract.HelloClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Configuration
public class InvokerController {

	@Autowired
	private PersonClient personClient;
	
	@RequestMapping(value = "/invokeHello", method = RequestMethod.GET)
	public String invokeHello() {
		return personClient.hello();
	}

	@RequestMapping(value = "/router", method = RequestMethod.GET, 
			produces = MediaType.APPLICATION_JSON_VALUE)
	@ResponseBody
	public String router() {
		// 调用服务提供者的接口
		Person p = personClient.getPerson(2);
		return p.getMessage();
	}
	
	@Autowired
	private HelloClient helloClient;
	
	@RequestMapping(value = "/testContract", method = RequestMethod.GET, 
			produces = MediaType.APPLICATION_JSON_VALUE)
	@ResponseBody
	public String testContract() {
		String springResult = helloClient.springHello();
		System.out.println("使用 @RequestMapping 注解的接口返回结果:" + springResult);
		String myResult = helloClient.myHello();
		System.out.println("使用 @MyUrl 注解的接口返回结果:" + myResult);
		return "";
	}
	
	/**
	 * 测试请求拦截器
	 */
	@RequestMapping(value = "/testInterceptors", method = RequestMethod.GET, 
			produces = MediaType.APPLICATION_JSON_VALUE)
	@ResponseBody
	public String testInterceptors() {
		String springResult = helloClient.springHello();
		return springResult;
	}
}

自定义的注解以及拦截器如下:

@MyUrl.java

package org.crazyit.cloud.contract;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 自定义的注解
 * @author 杨恩雄
 *
 */
@Target(METHOD)
@Retention(RUNTIME)
public @interface MyUrl {

	// 定义url与method属性
	String url();
	String method();
}

package org.crazyit.cloud.contract;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * 客户端接口
 * @author 杨恩雄
 *
 */
@FeignClient(name = "spring-feign-provider")
public interface HelloClient {
	
	@MyUrl(method = "GET", url = "/hello")
	String myHello();
	
	@RequestMapping(method = RequestMethod.GET, value = "/hello")
	String springHello();
}

package org.crazyit.cloud.contract;

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import feign.Contract;
import feign.RequestInterceptor;
import feign.RequestTemplate;

@Configuration
public class MyConfig {

	/**
	 * 返回一个自定义的注解翻译器
	 */
    @Bean
    public Contract feignContract() {
        return new MyContract();
    }
    
    @Bean
    public RequestInterceptor getRequestInterceptorsA() {
    	return new RequestInterceptor() {

			public void apply(RequestTemplate template) {
				System.out.println("这是第一个请求拦截器");
			}    		
    	};
    }
    
    @Bean
    public RequestInterceptor getRequestInterceptorsB() {
    	return new RequestInterceptor() {
			public void apply(RequestTemplate template) {
				System.out.println("这是第二个请求拦截器");
			}
    	};
    }
}

package org.crazyit.cloud.contract;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.springframework.cloud.netflix.feign.support.SpringMvcContract;

import feign.MethodMetadata;

/**
 * 自定义Contract
 * @author 杨恩雄
 */
public class MyContract extends SpringMvcContract {

	/**
	 * 用于处理方法级的注解
	 */
	protected void processAnnotationOnMethod(MethodMetadata data,
			Annotation annotation, Method method) {
		// 调用父类的方法,吗时支持 @RequestMapping 注解
		super.processAnnotationOnMethod(data, annotation, method);	
		// 是MyUrl注解才进行处理
		if(MyUrl.class.isInstance(annotation)) {
			// 获取注解的实例
			MyUrl myUrlAnn = method.getAnnotation(MyUrl.class);
			// 获取配置的HTTP方法
			String httpMethod = myUrlAnn.method();
			// 获取服务的url
			String url = myUrlAnn.url();
			// 将值设置到模板中
			data.template().method(httpMethod);
			data.template().append(url);
		}
	}
}

访问 http://localhost:9000/router
可以看到: 交替输出 8080 和 8081
image.png
image.png

访问: http://localhost:9000/testInterceptors
输出
这是第一个请求拦截器
这是第二个请求拦截器

访问 http://localhost:9000/testContract 输出:

这是第一个请求拦截器
这是第二个请求拦截器
使用 @RequestMapping 注解的接口返回结果:Hello World
这是第一个请求拦截器
这是第二个请求拦截器
使用 @MyUrl 注解的接口返回结果:Hello World

可见拦截器类 MyConfig.java 不管是对springCloud默认的Feign 还是对手写注解的@MyUrl都生效了。

SpringCloud为Feign提供了各种默认属性,以下的Bean

解码器 (Decoder): Bean的名称为: feignDecoder, ResponseEntityDecoder类。
编码器(Encoder): Bean的名称为: feignEncoder, SpringEncoder类.
日志 (Logger): Bean的名称为: feignLogger, Slf4jLogger类.
注解翻译器(Contract): Bean的名称为: feignContract, SpringMvcContract类.
Feign实例的创建者(Feign.Builder): Bean的名称为: feignBuilder, HystrixFeign.Builder类。
Feign客户端(Client): Bean的名称为 feignClient, LoadBalancerFeignClient类.

可选配置

以下配置spring并没有提供默认的Bean

  • Logger.Level:接口日志的记录级别,相当于调用了Feign.Builder的logLevel方法。
  • Retryer: 重试处理器,相当于调用了Feign.Builder的retryer方法
  • ErrorDecoder: 异常解码器,相当于调用了Feign.Builder的errorDecoder方法
  • Request.Options:设置请求的配置项,相当于调用了Feign.Builder的options方法
  • Collection:设置请求拦截器,相当于调用了Feign.Builder的requestInterceptors方法
  • SetterFactory:该配置与Hystrix框架相关,

压缩配置

Feign支持对请求和响应进行压缩处理,默认使用GZIP进行压缩,压缩操作在Feign的请求拦截器中实现,可以在配置文件中加入以下配置:

  • feign.compression.request.enabled: 设置为true开启请求压缩
  • feign.compression.response.enabled:设置为true开启响应压缩.
  • feign.compression.request.mime-types:数据类型列表,默认值为text/html,application/xml,application/json
  • feign.compression.request.min-request-size:设置请求内容的最小阈值,默认为2048.

补充:

实际项目中可能自定义很多配置比如oauth权限认证,以及日志,解码器等等,示例如下:

使用类

package com.finup.mobile.client;

import com.finup.mobile.model.common.request.RequestVo;
import com.finup.mobile.vo.request.CommonCallRecordReqVo;
import com.finup.mobile.vo.request.CommonMsgRecordReqVo;
import com.finup.mobile.vo.request.CommonWxAddFriendReceiveVo;
import com.finup.mobile.vo.request.CommonWxAddFriendResultVo;
import com.finup.mobile.vo.request.CommonWxAgreeFriendResultVo;
import com.finup.mobile.vo.request.CommonWxCustomerAgreeFriendReceiveVo;
import com.finup.mobile.vo.request.CommonWxMsgRecordVo;
import com.finup.mobile.vo.response.chedai.CheDaiResult;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.List;

/**
 * 车贷service
 *
 */
@FeignClient(name = "chedai", url = "${chedai.url}")
public interface CheDaiService {

    /**
     * 推送通话记录
     * @param request CallRecordReqVo
     * @return CheDaiResult
     */
    @RequestMapping(value = "/xxx/v1/xxx", method = RequestMethod.POST,
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    CheDaiResult<String> pushCallRecord(@RequestBody List<CommonCallRecordReqVo> request);

  
}

相关配置类如下:

package com.finup.mobile.config;

import feign.Feign;
import feign.InvocationHandlerFactory;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.security.oauth2.client.feign.OAuth2FeignRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import javax.annotation.Resource;
import javax.validation.constraints.Size;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.UUID;

import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;

/**
 *  FeiginClientAutoConfig
 */
@Slf4j
@Configuration
@ConditionalOnBean(OAuth2RestTemplate.class)
@EnableFeignClients(basePackages = "com.finup.mobile.client")
public class FeiginClientAutoConfig extends WebMvcConfigurerAdapter {

    private static final String X_EVENT_HTTP_KEY = "x_event_http_key";
    private static final String X_PATH = "x_path";

    @Bean
    public feign.Logger.Level feignLoggerLevel() {
        return feign.Logger.Level.FULL;
    }

    @Resource(name = "renMaiRestTemplate")
    private OAuth2RestTemplate oAuth2RestTemplate;

    @Bean
    public OAuth2FeignRequestInterceptor oAuth2FeignRequestInterceptor() {
        return new OAuth2FeignRequestInterceptor(oAuth2RestTemplate.getOAuth2ClientContext(),
                oAuth2RestTemplate.getResource());
    }

    @Bean
    public feign.Logger logTraceLogger() {
        return new CustomFeignLogger();
    }

    @Bean
    public Feign.Builder mdcLogBuilder() {
        return new Feign.Builder().invocationHandlerFactory(invocationHandlerFactory()).errorDecoder(new PhoneErrorDecoder());
    }
    /**
     * 添加spring mvc的拦截器,将来可以跟feign配置拦截器分离
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new EventHandlerInterceptor());
    }

    @Bean
    public InvocationHandlerFactory invocationHandlerFactory() {
        return (target, dispatch) -> (InvocationHandler) (proxy, method, args) -> {
            if ("equals".equals(method.getName())) {
                try {
                    Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0])
                            : null;
                    return equals(otherHandler);
                } catch (IllegalArgumentException e) {
                    return false;
                }
            } else if ("hashCode".equals(method.getName())) {
                return hashCode();
            } else if ("toString".equals(method.getName())) {
                return toString();
            }
            try {
                RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
                MDC.put(X_PATH, methodMapping.value()[0]);
            } catch (Exception ex) {
                log.error("", ex);
            }
            String requestResponseKey = UUID.randomUUID().toString().replace("-", "");
            MDC.put(X_EVENT_HTTP_KEY, requestResponseKey);
            try {
                return dispatch.get(method).invoke(args);
            } finally {
                MDC.remove(X_EVENT_HTTP_KEY);
            }
        };
    }
}

package com.finup.mobile.config;

import feign.Request;
import feign.Response;
import feign.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Collection;
import java.util.Formatter;
import java.util.LinkedHashMap;
import java.util.Map;

import static feign.Util.UTF_8;
import static feign.Util.decodeOrDefault;

public class CustomFeignLogger extends feign.Logger {

    private final Logger logger;

    public CustomFeignLogger() {
        this(feign.Logger.class);
    }

    public CustomFeignLogger(Class<?> clazz) {
        this(LoggerFactory.getLogger(clazz));
    }

    public CustomFeignLogger(String name) {
        this(LoggerFactory.getLogger(name));
    }

    CustomFeignLogger(Logger logger) {
        this.logger = logger;
    }


    /**
     * Override to log requests and responses using your own implementation. Messages will be http
     * request and response text.
     *
     * @param configKey
     * @param format    {@link Formatter format string}
     * @param args      arguments applied to {@code format}
     */
    @Override
    protected void log(String configKey, String format, Object... args) {
        if (logger.isInfoEnabled()) {
            logger.info(String.format(methodTag(configKey) + format, args));
        }
    }

    @Override
    protected void logRequest(String configKey, Level logLevel, Request request) {
        if (logger.isInfoEnabled()) {
            log(configKey, "---> %s %s HTTP/1.1", request.method(), request.url());
            if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {
                int bodyLength = 0;
                if (request.body() != null) {
                    bodyLength = request.body().length;
                    if (logLevel.ordinal() >= Level.FULL.ordinal()) {
                        String
                                bodyText =
                                request.charset() != null ? new String(request.body(), request.charset()) : null;
                        log(configKey, "");
                        log(configKey, "%s", bodyText != null ? bodyText : "Binary data");
                    }
                }
                log(configKey, "---> END HTTP (%s-byte body)", bodyLength);
            }
        }
    }

    @Override
    protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime)
            throws IOException {

        Map<String, Collection<String>> headers = new LinkedHashMap<String, Collection<String>>();
        for (String headerField : response.headers().keySet()) {
            headers.put(headerField, response.headers().get(headerField));
        }
        if (logger.isInfoEnabled()) {
            String reason = response.reason() != null && logLevel.compareTo(Level.NONE) > 0 ?
                    " " + response.reason() : "";
            int status = response.status();
            log(configKey, "<--- HTTP/1.1 %s%s (%sms)", status, reason, elapsedTime);
            if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {
                int bodyLength = 0;
                if (response.body() != null && !(status == 204 || status == 205)) {
                    // HTTP 204 No Content "...response MUST NOT include a message-body"
                    // HTTP 205 Reset Content "...response MUST NOT include an entity"
                    if (logLevel.ordinal() >= Level.FULL.ordinal()) {
                        log(configKey, "");
                    }
                    byte[] bodyData = Util.toByteArray(response.body().asInputStream());
                    bodyLength = bodyData.length;
                    if (logLevel.ordinal() >= Level.FULL.ordinal() && bodyLength > 0) {
                        log(configKey, "%s", decodeOrDefault(bodyData, UTF_8, "Binary data"));
                    }
                    log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
                    return response.toBuilder().body(bodyData).build();
                } else {
                    log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
                }
            }
            return response;
        } else {
            return response;
        }
    }
}

package com.finup.mobile.config;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.finup.mobile.common.Constants;
import com.finup.mobile.model.RequestLogWithBLOBs;
import com.finup.mobile.model.common.daihou.request.DHReqBaseVo;
import com.finup.mobile.utils.DataUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 *  EventHandlerInterceptor
 */
@Slf4j
public class EventHandlerInterceptor implements HandlerInterceptor {



    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView){
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    }

}

package com.finup.mobile.config;

import feign.FeignException;
import feign.Response;
import feign.codec.ErrorDecoder;

public class PhoneErrorDecoder extends ErrorDecoder.Default {

    public PhoneErrorDecoder() {
        super();
    }

    @Override
    public Exception decode(String methodKey, Response response) {
        Exception exception = super.decode(methodKey, response);
        if (exception instanceof FeignException) {
            exception = PhoneFeignException.errorStatus(methodKey, response);
        }
        return exception;
    }
}

package com.finup.mobile.config;

import feign.FeignException;
import feign.Response;
import feign.Util;

import java.io.IOException;

import static java.lang.String.format;

public class PhoneFeignException extends FeignException {

    protected PhoneFeignException(String message, Throwable cause) {
        super(message, cause);
    }

    protected PhoneFeignException(String message) {
        super(message);
    }

    protected PhoneFeignException(int status, String message) {
        super(status, message);
    }

    public static FeignException errorStatus(String methodKey, Response response) {
        String message = format("status %s reading %s url %s", response.status(), methodKey, response.request().url());
        try {
            if (response.body() != null) {
                String body = Util.toString(response.body().asReader());
                message += "; content:\n" + body;
            }
        } catch (IOException ignored) {
        }
        return new PhoneFeignException(response.status(), message);
    }

}


标题:SpringCloud系列--4.SpringCloud使用Feign
作者:码农路上
地址:http://wujingjian.club/articles/2020/03/17/1584435979954.html