Feign系列--全局异常处理
程序员文章站
2022-07-15 13:07:26
...
其他网址
简介
之前介绍过feign的fallback,它可以出现异常时进行处理,使得代码出现问题能继续往下走,而不是直接报错。但这个的使用场景很少,一般都是出错直接抛异常的。
本文介绍feign的全局异常处理,开发中更加常用。(基本与SpringMVC的全局异常相同,见:
SpringMVC系列--高级注解_feiying0canglang的博客-CSDN博客)
公用代码
全局异常
package com.example.product.advice;
import com.example.product.entity.ResultWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@ControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler({Exception.class})
@ResponseBody
public ResultWrapper handleException(HttpServletRequest request, Exception e) throws Exception {
// e.printStackTrace();
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
log.error("全局异常message:" + this.getClass().getPackage().getName() + e.getMessage());
log.error("全局异常cause:" + this.getClass().getPackage().getName() + e.getCause());
return ResultWrapper.failure()
.message("全局异常message:" + this.getClass().getPackage().getName() + ":" + e.getMessage())
.object("全局异常cause:" + this.getClass().getPackage().getName() + e.getCause());
}
}
返回值包装类
package com.example.product.entity;
import lombok.Data;
@Data
public class ResultWrapper {
private boolean success;
private String message;
private Object object;
public ResultWrapper() {
}
public ResultWrapper(boolean success) {
this.success = success;
}
public static ResultWrapper success(boolean success) {
return new ResultWrapper(success);
}
public static ResultWrapper success() {
return success(true);
}
public static ResultWrapper failure() {
return success(false);
}
public <T extends ResultWrapper> T message(String message) {
this.message = message;
return (T) this;
}
public <T extends ResultWrapper> T object(Object object) {
this.object = object;
return (T) this;
}
public <T extends ResultWrapper> T object(Object... object) {
this.object = object;
return (T) this;
}
}
用户实体类
package com.example.product.entity;
import lombok.Data;
import java.io.Serializable;
@Data
public class User {
private Long id;
private String userName;
private Integer age;
}
调用端(Product)
feign配置
package com.example.product.config;
import com.example.product.feign.FeignResultWrapperDecoder;
import feign.Logger;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public Encoder feignEncoder() {
return new SpringEncoder(messageConverters);
}
@Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new FeignResultWrapperDecoder(messageConverters));
}
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
解码器
package com.example.product.feign;
import com.alibaba.fastjson.JSON;
import com.example.product.entity.ResultWrapper;
import feign.FeignException;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.HttpMessageConverterExtractor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
@Slf4j
public class FeignResultWrapperDecoder implements Decoder {
private ObjectFactory<HttpMessageConverters> messageConverters;
public FeignResultWrapperDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {
this.messageConverters = messageConverters;
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
if (type instanceof Class || type instanceof ParameterizedType
|| type instanceof WildcardType) {
if (type == ResultWrapper.class) {
HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor(
type, this.messageConverters.getObject().getConverters());
return extractor.extractData(new FeignResponseAdapter(response));
} else {
ResultWrapper wrapper = (ResultWrapper) decode(response, ResultWrapper.class);
if (wrapper.isSuccess()) {
if (type == Object.class) {
return wrapper.getObject();
}
return JSON.parseObject(JSON.toJSONString(wrapper.getObject()), type);
} else {
log.error("wrapper.getMessage():" + wrapper.getMessage());
throw new RuntimeException(wrapper.getMessage());
}
}
}
throw new DecodeException(response.status(),
"type is not an instance of Class or ParameterizedType: " + type);
}
private final class FeignResponseAdapter implements ClientHttpResponse {
private final Response response;
private FeignResponseAdapter(Response response) {
this.response = response;
}
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.valueOf(this.response.status());
}
@Override
public int getRawStatusCode() throws IOException {
return this.response.status();
}
@Override
public String getStatusText() throws IOException {
return this.response.reason();
}
@Override
public void close() {
try {
this.response.body().close();
} catch (IOException ex) {
// Ignore exception on close...
}
}
@Override
public InputStream getBody() throws IOException {
return this.response.body().asInputStream();
}
@Override
public HttpHeaders getHeaders() {
return getHttpHeaders(this.response.headers());
}
}
private HttpHeaders getHttpHeaders(Map<String, Collection<String>> headers) {
HttpHeaders httpHeaders = new HttpHeaders();
for (Map.Entry<String, Collection<String>> entry : headers.entrySet()) {
httpHeaders.put(entry.getKey(), new ArrayList<>(entry.getValue()));
}
return httpHeaders;
}
}
feignclient
package com.example.product.feign;
import com.example.product.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
//@FeignClient(name = "user", fallbackFactory = FeignFallbackFactory.class)
@FeignClient(name = "user")
public interface UserFeignClient {
@GetMapping("/user/{id}")
User getUser(@PathVariable("id") Long id);
}
ProductController
package com.example.product.controller;
import com.example.product.entity.User;
import com.example.product.feign.UserFeignClient;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
UserFeignClient userFeignClient;
@GetMapping("/feign")
public User testFeign(){
User user = userFeignClient.getUser(1L);
return user;
}
}
被调用端(User)
package com.example.user.controller;
import com.example.user.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.logging.Logger;
//必须用这个,不能用@Controller。
@RestController
@RequestMapping("/user")
public class UserController {
private Logger logger = Logger.getLogger(String.valueOf(getClass()));
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
public User getUser(@PathVariable("id") Long id) {
ServiceInstance serviceInstance = discoveryClient.getInstances("USER").get(0);
logger.info("[" + serviceInstance.getServiceId() + "]:" +
serviceInstance.getHost() + ":" + serviceInstance.getPort());
User user = new User();
user.setId(id);
user.setUserName("user_name_" + id);
user.setAge(20);
int i = 1/0;
return user;
}
}
测试
postman访问:http://localhost:9002/product/feign
postman结果
{
"success": false,
"message": "全局异常message:com.example.product.advice:UserFeignClient#getUser(Long) failed and no fallback available.",
"object": "全局异常cause:com.example.product.advicefeign.codec.DecodeException: 全局异常message:com.example.user.advice:/ by zero"
}
Product结果
2020-09-18 22:27:49.875 ERROR 4940 --- [ hystrix-user-1] c.e.p.feign.FeignResultWrapperDecoder : wrapper.getMessage():全局异常message:com.example.user.advice:/ by zero
2020-09-18 22:27:49.886 ERROR 4940 --- [nio-9002-exec-1] c.e.product.advice.ExceptionAdvice : 全局异常message:com.example.product.adviceUserFeignClient#getUser(Long) failed and no fallback available.
2020-09-18 22:27:49.886 ERROR 4940 --- [nio-9002-exec-1] c.e.product.advice.ExceptionAdvice : 全局异常cause:com.example.product.advicefeign.codec.DecodeException: 全局异常message:com.example.user.advice:/ by zero
User结果
2020-09-18 22:27:49.840 ERROR 23736 --- [nio-8001-exec-1] com.example.user.advice.ExceptionAdvice : 全局异常message:com.example.user.advice/ by zero
2020-09-18 22:27:49.840 ERROR 23736 --- [nio-8001-exec-1] com.example.user.advice.ExceptionAdvice : 全局异常cause:com.example.user.advicenull
上一篇: hystrix 总结
下一篇: VIM中鼠标右键无法复制的解决方法