1. 程式人生 > >java中動態代理實現基於feign的第三方服務的呼叫

java中動態代理實現基於feign的第三方服務的呼叫

一,Feign靜態工廠

/** * <p>
* 服務預設連線延遲為10s,讀取延遲為60s。如果有大的批量任務,或者特殊場景,請自行設定 {@link Request.Options}
 * <p>
* 服務預設retry策略為失敗之後不重試。對於某些讀介面,可以定義自己的retry策略,比如重試三次
 * <p>
* 定義自己的requestInterceptors,可以攔截服務的http請求,方便框架上做一些事。
 * <p>
* 定義服務的ServiceEndPoint值時,可以定義到package或class級別。預設優先從class級別讀取,讀取不到時,再去讀package的。
* <p> * 注意:service的例項是有快取的。如果需要定義同一個serviceClazz的不同例項,需要呼叫removeCache方法 * <p> * 注意所有的配置是到Class級別,而不是method級別 * <p> */ public class FeignServiceFactory { private static final Logger LOGGER = LoggerFactory.getLogger(FeignServiceFactory.class); private static final ConcurrentMap<Class,
Object> SERVICE_CACHE = new ConcurrentHashMap<>(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() .setSerializationInclusion(JsonInclude.Include.NON_NULL) .configure(SerializationFeature.INDENT_OUTPUT, true) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
, false); public static void removeCache(final Class<?> serviceClazz) { SERVICE_CACHE.remove(serviceClazz); } public static <T> T newInstance(final Class<T> serviceClazz) { return newInstance(serviceClazz, null); } public static <T> T newInstance(final Class<T> serviceClazz, Request.Options options) { return newInstance(serviceClazz, options, null, null); } public static <T> T newInstance(final Class<T> serviceClazz, Request.Options options, Iterable<RequestInterceptor> requestInterceptors, Retryer retryer) { Object serviceInstance = SERVICE_CACHE.get(serviceClazz); if (serviceInstance != null) { return (T) serviceInstance; } T newServiceInstance = create0(serviceClazz, options, requestInterceptors, retryer); serviceInstance = SERVICE_CACHE.putIfAbsent(serviceClazz, newServiceInstance); if (serviceInstance == null) { serviceInstance = newServiceInstance; } return (T) serviceInstance; } private static <T> T create0(final Class<T> serviceClazz, Request.Options options, Iterable<RequestInterceptor> requestInterceptors, Retryer retryer) { if (options == null) { //預設connectTimeoutMillis=10s,readTimeoutMillis=60s options = new Request.Options(); } if (requestInterceptors == null) { requestInterceptors = new ArrayList<>(0); } if (retryer == null) { retryer = new Retryer.Default(0, 0, -1); } ServiceEndPoint annotation = getServiceEndPoint(serviceClazz); String endPoint = annotation.value(); Objects.requireNonNull(endPoint, "endPoint cannot be null"); String baseUrl = EndPointConfigs.getBaseUrl(endPoint); LOGGER.info("create service instance of {}, endPoint {}, baseUrl {},", serviceClazz, endPoint, baseUrl); T serviceInstance = Feign.builder() .logger(new Slf4jLogger()) .contract(new MyContract()) .options(options) .requestInterceptors(requestInterceptors) .logLevel(feign.Logger.Level.NONE) .decoder(new OptionalDecoder(new JacksonDecoder(OBJECT_MAPPER))) .encoder(new FormEncoder(new JacksonEncoder(OBJECT_MAPPER))) .client(new OkHttpClientWrapper(HttpClientSingleton.getOkHttpClient())) .retryer(retryer)//預設不retry .target(serviceClazz, baseUrl); return serviceInstance; } /** * 找endPoint,優先在類上找,其次才是包 * * @param serviceClazz * @param <T> * @return */ private static <T> ServiceEndPoint getServiceEndPoint(final Class<T> serviceClazz) { ServiceEndPoint serviceEndPoint = serviceClazz.getAnnotation(ServiceEndPoint.class); if (serviceEndPoint != null) { return serviceEndPoint; } serviceEndPoint = serviceClazz.getPackage().getAnnotation(ServiceEndPoint.class); if (serviceEndPoint != null) { return serviceEndPoint; } throw new RuntimeException("cannot find the endPoint"); } private static class MyContract extends Contract.Default { @Override protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) { super.processAnnotationOnMethod(data, methodAnnotation, method); Class<? extends Annotation> annotationType = methodAnnotation.annotationType(); if (annotationType == PhpApiName.class) { String apiName = PhpApiName.class.cast(methodAnnotation).value(); data.template().body("apiOutput=JSON&apiName=" + UrlCoder.encode(apiName)); } } } }

二,動態代理

public class ThirdpartyServiceInvoker implements InvocationHandler{

    private static final Logger LOGGER = LoggerFactory.getLogger(ThirdpartyServiceInvoker.class);
    private Object obj;
    private String refService;
    public ThirdpartyServiceInvoker(Object obj,String refService) {
        this.obj = obj;
        this.refService = refService;
}

    @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        try{
            if(ThirdpartyServiceUtil.BEFORE_TIMEOUT){
                throw new AssetBusinessRuntimeException(MessageDict.DEP_ERROR);
}

            result = method.invoke(obj,args);
            if(ThirdpartyServiceUtil.AFTER_TIMEOUT){
                throw new AssetBusinessRuntimeException(MessageDict.DEP_ERROR);
}
        } catch (Exception ex) {
            if(!"toString".equals(method.getName())){
                String invokeDesc = refService+"."+method.getName();
LOGGER.info("=====>>>>>third party service timeout:{}",method );
QiyeWeixinTool.sendMessageToGroupId(Constant.P_GROUP, "呼叫第三方服務異常:"+invokeDesc, ex);
}
            throw new AssetBusinessRuntimeException(MessageDict.DEP_ERROR);
}
        return result;
}
}

三,第三方介面栗子

@Headers("Content-Type: application/x-www-form-urlencoded")
public interface MemberService {/**
     * 根據使用者名稱獲取member
     *
     * @param username
* @return
*/
@RequestLine("POST /member/get")
    Member getMemberByUsername(@Param("username") String username);
@Headers({"Content-Type: application/json"})
    @RequestLine("POST /member/getMembersByIds")
    Map<Integer,Member> getMembersByIds(@Param("ids") List<Integer> ids);
}

四,依賴第三方服務栗子

@Configuration
public class DependencyServiceConfig {
    @Bean
MemberService memberService() {
        return (MemberService) ThirdpartyServiceUtil.getInstance(FeignServiceFactory.newInstance(MemberService.class),"memberService");
}
}