java-Feign(简化Java HTTP客户端开发的工具)

本文由调试Feign官网 example-wikipedia案例来理解Feign
本次运行demo目录如图
java-Feign(简化Java HTTP客户端开发的工具)_第1张图片
GSON解析参考https://jingyan.baidu.com/article/e8cdb32b619f8437042bad53.html
WikipediaExample.java:

package feign.example.wikipedia;
import ...;

public class WikipediaExample {

  static ResponseAdapter pagesAdapter = new ResponseAdapter() {

    @Override
    protected String query() {
      return "pages";
    }

    @Override
    protected Page build(JsonReader reader) throws IOException {
      Page page = new Page();
      while (reader.hasNext()) {
        String key = reader.nextName();
        if (key.equals("pageid")) {
          page.id = reader.nextLong();
        } else if (key.equals("title")) {
          page.title = reader.nextString();
        } else {
          reader.skipValue();
        }
      }
      return page;
    }
  };

  public static void main(String... args) throws InterruptedException {
    Gson gson = new GsonBuilder()
        .registerTypeAdapter(new TypeToken>() {
        }.getType(), pagesAdapter)
        .create();

    Wikipedia wikipedia = Feign.builder()
        .decoder(new GsonDecoder(gson))
        .logger(new Logger.ErrorLogger())
        .logLevel(Logger.Level.BASIC)
        .target(Wikipedia.class, "https://en.wikipedia.org");

    System.out.println("Let's search for PTAL!");
    Iterator pages = lazySearch(wikipedia, "PTAL");
    while (pages.hasNext()) {
      System.out.println(pages.next().title);
    }
  }

  /**
   * this will lazily continue searches, making new http calls as necessary.
   *
   * @param wikipedia used to search
   * @param query     see {@link Wikipedia#search(String)}.
   */
  static Iterator lazySearch(final Wikipedia wikipedia, final String query) {
    final Response first = wikipedia.search(query);
    if (first.nextOffset == null) {
      return first.iterator();
    }
    return new Iterator() {
      Iterator current = first.iterator();
      Long nextOffset = first.nextOffset;

      @Override
      public boolean hasNext() {
        while (!current.hasNext() && nextOffset != null) {
          System.out.println("Wow.. even more results than " + nextOffset);
          Response nextPage = wikipedia.resumeSearch(query, nextOffset);
          current = nextPage.iterator();
          nextOffset = nextPage.nextOffset;
        }
        return current.hasNext();
      }

      @Override
      public Page next() {
        return current.next();
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException();
      }
    };
  }

  public static interface Wikipedia {

    @RequestLine("GET /w/api.php?action=query&continue=&generator=search&prop=info&format=json&gsrsearch={search}")
    Response search(@Param("search") String search);

    @RequestLine("GET /w/api.php?action=query&continue=&generator=search&prop=info&format=json&gsrsearch={search}&gsroffset={offset}")
    Response resumeSearch(@Param("search") String search, @Param("offset") long offset);
  }

  static class Page {

    long id;
    String title;
  }

  public static class Response<X> extends ArrayList<X> {

    /**
     * when present, the position to resume the list.
     */
    Long nextOffset;
  }
}

1.1 Feign.build方法

public Builder() {
            this.logLevel = Level.NONE;
            this.contract = new Default();
            this.client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);
            this.retryer = new feign.Retryer.Default();
            this.logger = new NoOpLogger();
            this.encoder = new feign.codec.Encoder.Default();
            this.decoder = new feign.codec.Decoder.Default();
            this.errorDecoder = new feign.codec.ErrorDecoder.Default();
            this.options = new Options();
            this.invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();
        }

1.1 Feign.decoder

public Feign.Builder decoder(Decoder decoder) {
            this.decoder = decoder;
            return this;
        }

1.3 Feign.logger方法

public Feign.Builder logger(Logger logger) {
            this.logger = logger;
            return this;
        }

1.4 Feign.logLevel方法

public Feign.Builder logLevel(Level logLevel) {
            this.logLevel = logLevel;
            return this;
        }

1.5 Feign.target方法
1.51 Feign.build().**.target(apiType, url)

 public  T target(Class apiType, String url) {
            return this.target(new HardCodedTarget(apiType, url));
        }

1.52 然后,调用this.target(**)
返回apiType类型的target

        public  T target(Target target) {
            return this.build().newInstance(target);
        }

        public Feign build() {
        //根据Feign的配置属性来初始化MethodHandlerFactory
            Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404);
            //options是用于设置Client中http request的一些参数
            ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.errorDecoder, synchronousMethodHandlerFactory);
            //返回Feign反射管理对象
            return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory);
        }

1.53 调用ReflectiveFeign.newInstance
创建ReflectiveFeign反射代理

 public  T newInstance(Target target) {
 //参数target本质为new HardCodedTarget(apiType, url)
 //返回Map对象,封装其中key为方法名,value为方法处理器
        Map nameToHandler = this.targetToHandlersByName.apply(target);
        Map methodToHandler = new LinkedHashMap();
        List defaultMethodHandlers = new LinkedList();
        //根据HardCodedTarget获取当前接口的所有方法
        Method[] var5 = target.type().getMethods();
        //var6为方法个数
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method method = var5[var7];
            if (method.getDeclaringClass() != Object.class) {
            //判断是否为默认方法toString(),equal()等
                if (Util.isDefault(method)) {
                //构建DefaultMethodHandler
                    DefaultMethodHandler handler = new DefaultMethodHandler(method);
                    //添加方法处理器到方法处理器集合(List)
                    defaultMethodHandlers.add(handler);
                    //添加配对关系key为Method,value为MethodHandler
                    methodToHandler.put(method, handler);
                } else {
                //如果不是默认方法,为其他的接口方法
                // Feign.configKey(target.type(), method)生成方法处理器对应的key(Wikipedia#search(String)),
                //并根据key获取方法处理器
                    methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
                }
            }
        }
    //创建FeignInvocationHandler
        InvocationHandler handler = this.factory.create(target, methodToHandler);
        //java创建代理对象,即接口代理
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        //默认方法
        Iterator var12 = defaultMethodHandlers.iterator(); 
        while(var12.hasNext()) {
        //如果有默认方法,将方法绑定到代理对象
            DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
            defaultMethodHandler.bindTo(proxy);
        }
        //最后,返回Target代理
        return proxy;
    }

//根据动态生成key
如:Wikipedia#search(String)

 public static String configKey(Class targetType, Method method) {
        StringBuilder builder = new StringBuilder();
        builder.append(targetType.getSimpleName());
        builder.append('#').append(method.getName()).append('(');
        Type[] var3 = method.getGenericParameterTypes();
        int var4 = var3.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Type param = var3[var5];
            param = Types.resolve(targetType, targetType, param);
            builder.append(Types.getRawType(param).getSimpleName()).append(',');
        }

        if (method.getParameterTypes().length > 0) {
            builder.deleteCharAt(builder.length() - 1);
        }

        return builder.append(')').toString();
    }

java-Feign(简化Java HTTP客户端开发的工具)_第2张图片

 public  T target(Target target) {
            return this.build().newInstance(target);
        }

然后,调用InvocationHandlerFactory .create创建FeignInvocationHandler

public interface InvocationHandlerFactory {
    InvocationHandler create(Target var1, Map var2);

    public static final class Default implements InvocationHandlerFactory {
        public Default() {
        }
//根据target,methodToMethod构建FeignInvocationHandler
        public InvocationHandler create(Target target, Map dispatch) {
            return new FeignInvocationHandler(target, dispatch);
        }
    }

    public interface MethodHandler {
        Object invoke(Object[] var1) throws Throwable;
    }
}

1.6 创建Target代理对象InvocationHandler
java-Feign(简化Java HTTP客户端开发的工具)_第3张图片

1.7 target方法结束,返回代理对象proxy,赋值给target(此处我命名为WikipediaProxy)

2 调用方法,由代理WikipediaProxy执行方法


    System.out.println("Let's search for PTAL!");
    //根据参数调用target接口的方法lazySearch
    Iterator pages = lazySearch(wikipedia, "PTAL");
    while (pages.hasNext()) {
      System.out.println(pages.next().title);
    }

2.1 代理WikipediaProxy自动调用invoke方法

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 //判断是不是equals/toString等方法
            if (!"equals".equals(method.getName())) {
                if ("hashCode".equals(method.getName())) {
                    return this.hashCode();
                } else {
                    return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
                }
            } else {
                try {
                //获取方法
                    Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                    return this.equals(otherHandler);
                } catch (IllegalArgumentException var5) {
                    return false;
                }
            }
        }

调用过程如图:
java-Feign(简化Java HTTP客户端开发的工具)_第4张图片

3 运行结果

java-Feign(简化Java HTTP客户端开发的工具)_第5张图片
4 关于ResponseAdapter等待下篇分享
附上ResponseAdapter.java

package feign.example.wikipedia;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;

/**
 * 自定义适配器对象
 * 主要继承read和write方法
 */
abstract class ResponseAdapter<X> extends TypeAdapter<WikipediaExample.Response<X>> {

  /**
   * name of the key inside the {@code query} dict which holds the elements desired.  ex. {@code
   * pages}.
   */
  protected abstract String query();

  /**
   * Parses the contents of a result object. 


ex. If {@link #query()} is {@code pages}, * then this would parse the value of each key in the dict {@code pages}. In the example below, * this would first start at line {@code 3}.

*

   * "pages": {
   *   "2576129": {
   *     "pageid": 2576129,
   *     "title": "Burchell's zebra",
   * --snip--
   * 
*/
protected abstract X build(JsonReader reader) throws IOException; /** * the wikipedia api doesn't use json arrays, rather a series of nested objects. */ @Override public WikipediaExample.Response read(JsonReader reader) throws IOException { WikipediaExample.Response pages = new WikipediaExample.Response(); reader.beginObject(); while (reader.hasNext()) { String nextName = reader.nextName(); if ("query".equals(nextName)) { reader.beginObject(); while (reader.hasNext()) { if (query().equals(reader.nextName())) { reader.beginObject(); while (reader.hasNext()) { // each element is in form: "id" : { object } // this advances the pointer to the value and skips the key reader.nextName(); reader.beginObject(); pages.add(build(reader)); reader.endObject(); } reader.endObject(); } else { reader.skipValue(); } } reader.endObject(); } else if ("continue".equals(nextName)) { reader.beginObject(); while (reader.hasNext()) { if ("gsroffset".equals(reader.nextName())) { pages.nextOffset = reader.nextLong(); } else { reader.skipValue(); } } reader.endObject(); } else { reader.skipValue(); } } reader.endObject(); return pages; } @Override public void write(JsonWriter out, WikipediaExample.Response response) throws IOException { throw new UnsupportedOperationException(); } }

你可能感兴趣的:(mybatis)