不使用SpringBoot如何將原生Feign集成到Spring中來簡化http調用

Github 使用示例(https://github.com/cky-thinker/spring-feign-client/blob/master/README.md)

在微服務架構中,若是使用得是SpringCloud,那麼只須要集成SpringFeign就能夠了,SpringFeign能夠很友好的幫咱們進行服務請求,對象解析等工做。html

然而SpingCloud是依賴於SpringBoot的。在老的Spring項目中一般是沒有集成SpringBoot,那麼咱們又該如何使用Feign組件進行調用呢?java

這種狀況下就只能使用原生Feign了,Feign使用手冊:https://www.cnblogs.com/chenkeyu/p/9017996.htmlgit

使用原生Feign的兩個問題:

  1、原生Feign只能一次解析一個接口,生成對應的請求代理對象,若是一個包裏有多個調用接口就要屢次解析很是麻煩。github

  2、Feign生成的調用代理只是一個普通對象,該如何註冊到Spring中,以便於咱們可使用@Autowired隨時注入。spring

解決方案:

  1、針對屢次解析的問題,能夠經過指定掃描包路徑,而後對包中的類依次解析。使用工具:https://github.com/lukehutch/fast-classpath-scannerjson

  2、實現BeanFactoryPostProcessor接口,擴展Spring容器功能。api

具體代碼:

  maven依賴:

<dependency>
            <groupId>com.netflix.feign</groupId>
            <artifactId>feign-core</artifactId>
            <version>8.18.0</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.feign</groupId>
            <artifactId>feign-jackson</artifactId>
            <version>8.18.0</version>
        </dependency>
        <dependency>
            <groupId>io.github.lukehutch</groupId>
            <artifactId>fast-classpath-scanner</artifactId>
            <version>2.18.1</version>
        </dependency>

  自定義註解:在掃描接口的過程當中,能夠經過一個自定義註解,來區分Feign接口而且指定調用的服務Url

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;架構

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignApi {
/**
* 調用的服務地址
* @return
*/
String serviceUrl();
}app

  生成Feign代理並註冊到Spring實現類:

import feign.Feign;
import feign.Request;
import feign.Retryer;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class FeignClientRegister implements BeanFactoryPostProcessor{
    //掃描的接口路徑
    private String  scanPath="com.xxx.api";

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        List<String> classes = scan(scanPath);
        if(classes==null){
            return ;
        }
        System.out.println(classes);
        Feign.Builder builder = getFeignBuilder();
        if(classes.size()>0){
            for (String claz : classes) {
                Class<?> targetClass = null;
                try {
                    targetClass = Class.forName(claz);
                    String url=targetClass.getAnnotation(FeignApi.class).serviceUrl();
                    if(url.indexOf("http://")!=0){
                        url="http://"+url;
                    }
                    Object target = builder.target(targetClass, url);
                    beanFactory.registerSingleton(targetClass.getName(), target);
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }
    }

    public Feign.Builder getFeignBuilder(){
        Feign.Builder builder = Feign.builder()
                .encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder())
                .options(new Request.Options(1000, 3500))
                .retryer(new Retryer.Default(5000, 5000, 3));
        return builder;
    }

    public List<String> scan(String path){
        ScanResult result = new FastClasspathScanner(path).matchClassesWithAnnotation(FeignApi.class, (Class<?> aClass) -> {
        }).scan();
        if(result!=null){
            return result.getNamesOfAllInterfaceClasses();
        }
        return  null;
    }
}

調用接口編寫示例:

import com.xiaokong.core.base.Result;
import com.xiaokong.domain.DO.DeptRoom;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import com.xiaokong.register.FeignApi;

import java.util.List;

@FeignApi(serviceUrl = "http://localhost:8085")
public interface RoomApi {
    @Headers({"Content-Type: application/json","Accept: application/json"})
    @RequestLine("GET /room/selectById?id={id}")
    Result<DeptRoom> selectById(@Param(value="id") String id);

    @Headers({"Content-Type: application/json","Accept: application/json"})
    @RequestLine("GET /room/test")
    Result<List<DeptRoom>> selectList();
}

接口使用示例:

@Service
public class ServiceImpl{
    //將接口注入要使用的bean中直接調用便可
    @Autowired
    private RoomApi roomApi;
    

    @Test
    public void demo(){
        Result<DeptRoom> result = roomApi.selectById("1");
        System.out.println(result);
    }
}

 

注意事項:

1.若是接口返回的是一個複雜的嵌套對象,那麼必定要明確的指定泛型,由於Feign在解析複雜對象的時候,須要經過反射獲取接口返回對象內部的泛型類型才能正確使用Jackson解析。dom

若是不明確的指明類型,Jackson會將json對象轉換成一個LinkedHashMap類型。

2.若是你使用的是的Spring,又須要經過http調用別人的接口,均可以使用這個工具來簡化調用與解析的操做。

相關文章
相關標籤/搜索