本文由调试Feign官网 example-wikipedia案例来理解Feign
本次运行demo目录如图
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();
}
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
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;
}
}
}
3 运行结果
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();
}
}