feign的核心功能就是通过接口去访问网络资源,猜一下里面也是用动态代理来实现的,就跟Mybatis用接口去访问数据库一样,今天我们就来看下源码的处理,核心就一个包:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
先来看个demo:
//feign接口
public interface DemoClient {
@RequestLine("GET /hello/{username}")
public String hello(@Param("username") String username);
}
public class Demo {
public static void main(String[] args) {
DemoClient demoClient = Feign.builder()
.target(DemoClient.class, "http://localhost:9000");
//输出结果:com.sun.proxy.$Proxy3
System.out.println(demoClient.getClass().getName());
System.out.println(demoClient.hello("Joshua"));
}
}
以上打印出的demoClient的类名是:com.sun.proxy.$Proxy3,很显然是jdk的动态代理生成的类名。
首先看下Feign#builder:
public static Builder builder() {
//只是创建了一个Builder对象
return new Builder();
}
我们看下Builder里面都有啥东西:
public static class Builder {
private final List<RequestInterceptor> requestInterceptors =
new ArrayList<RequestInterceptor>();
private Logger.Level logLevel = Logger.Level.NONE;
private Contract contract = new Contract.Default();
private Client client = new Client.Default(null, null);
private Retryer retryer = new Retryer.Default();
private Logger logger = new NoOpLogger();
private Encoder encoder = new Encoder.Default();
private Decoder decoder = new Decoder.Default();
private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();
private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
private Options options = new Options();
private InvocationHandlerFactory invocationHandlerFactory =
new InvocationHandlerFactory.Default();
private boolean decode404;
private boolean closeAfterDecode = true;
private ExceptionPropagationPolicy propagationPolicy = NONE;
private boolean forceDecoding = false;
private List<Capability> capabilities = new ArrayList<>();
}
我们挑几个重要的来说一下他们都是干嘛的。
(1)requestInterceptors
这个就是拦截器,可以在请求之前设置请求头、设置请求体、设置参数、设置url等等,类型是:RequestInterceptor:
public interface RequestInterceptor {
void apply(RequestTemplate template);
}
RequestTemplate里面包含了最终用于创建Request的参数和配置,拦截器就是在往里面设置值。
(2)contract
private Contract contract = new Contract.Default();
默认是Contract.Default(),它主要是用来解析feign接口上的那些注解,比如:@QueryMap、@Param、@RequestLine、@Header、@Body、@HeaderMap等,以@Header为例:
public Default() {
super.registerClassAnnotation(Headers.class, (header, data) -> {
final String[] headersOnType = header.value();
checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.",
data.configKey());
final Map<String, Collection<String>> headers = toMap(headersOnType);
headers.putAll(data.template().headers());
data.template().headers(null); // to clear
data.template().headers(headers);
});
。。。。
}
做的事情就是把@Header(“name=value”)这里面的name=value取出来,重新设置到RequestTemplate里面。
(3)client
private Client client = new Client.Default(null, null);
public interface Client {
Response execute(Request request, Options options) throws IOException;
}
client是真正去执行request,得到response的客户端,它的入参是一个Request,这个Request是用RequestTemplate构造出来的。我们看下default处理:
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}
convertAndSend()就是发请求,主要包括创建HttpURLConnection或者HttpsURLConnection,设置连接的各种超时时间(来自于Options),设置请求头、请求体,然后请求服务端。
convertResponse()就是解析响应,解析响应码、响应头、响应体。
client里面的东西其实没啥可看的。
(4)encoder
private Encoder encoder = new Encoder.Default();
public interface Encoder {
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}
class Default implements Encoder {
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) {
if (bodyType == String.class) {
template.body(object.toString());
} else if (bodyType == byte[].class) {
template.body((byte[]) object, null);
} else if (object != null) {
throw new EncodeException(
format("%s is not a type supported by this encoder.", object.getClass()));
}
}
}
encoder是用来编码请求的,默认能处理String和byte[]数组类型的参数,最终是存放在RequestTemplate里面。
(5)decoder
public interface Decoder {
Object decode(Response response, Type type) throws IOException, DecodeException, FeignException;
public class Default extends StringDecoder {
@Override
public Object decode(Response response, Type type) throws IOException {
if (response.status() == 404 || response.status() == 204)
return Util.emptyValueOf(type);
if (response.body() == null)
return null;
if (byte[].class.equals(type)) {
return Util.toByteArray(response.body().asInputStream());
}
return super.decode(response, type);
}
}
}
decoder用来解码响应,默认可以返回字节数组和字符串。
(6)queryMapEncoder
private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();
它用来把@QueryMap注解标识的类的所有的字段都取出来放到url参数中。
(7)errorDecoder
异常处理
(8)invocationHandlerFactory
public interface InvocationHandlerFactory {
InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);
interface MethodHandler {
Object invoke(Object[] argv) throws Throwable;
}
static final class Default implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}
}
就是在这里面创建的动态代理类。
当客户端调用Feign.builder()的时候,其实就是去设置builder里面的这些参数的值,最终需要调用target方法去生成代理类:
/**
* apiType就是接口类,url就是服务端的地址
*/
public <T> T target(Class<T> apiType, String url) {
return target(new HardCodedTarget<T>(apiType, url));
}
HardCodedTarget用来根据RequestTemplate构造出Request:
public interface Target<T> {
public Request apply(RequestTemplate input);
}
public static class HardCodedTarget<T> implements Target<T> {
@Override
public Request apply(RequestTemplate input) {
if (input.url().indexOf("http") != 0) {
input.target(url());
}
return input.request();
}
}
继续看target方法:
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
先看下build()方法:
public Feign build() {
Client client = Capability.enrich(this.client, capabilities);
Retryer retryer = Capability.enrich(this.retryer, capabilities);
List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
Logger logger = Capability.enrich(this.logger, capabilities);
Contract contract = Capability.enrich(this.contract, capabilities);
Options options = Capability.enrich(this.options, capabilities);
Encoder encoder = Capability.enrich(this.encoder, capabilities);
Decoder decoder = Capability.enrich(this.decoder, capabilities);
InvocationHandlerFactory invocationHandlerFactory =
Capability.enrich(this.invocationHandlerFactory, capabilities);
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
public class ReflectiveFeign extends Feign {
ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
QueryMapEncoder queryMapEncoder) {
this.targetToHandlersByName = targetToHandlersByName;
this.factory = factory;
this.queryMapEncoder = queryMapEncoder;
}
}
就是构造Feign客户端对象ReflectiveFeign,并且添加默认值,注意下synchronousMethodHandlerFactory创建出来的MethodHandler的类型是SynchronousMethodHandler。handlersByName可以把feign接口解析成Map<String, MethodHandler>。
继续看newInstance(target):
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
//这个是处理default方法的
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
//解析出来<Method,MethodHandler>
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
//ReflectiveFeign.FeignInvocationHandler,具体是如何调用的,看下它的处理
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
这里就很清晰了,返回的是动态代理类。
先看下nameToHandler是咋构造出来的:
public Map<String, MethodHandler> apply(Target target) {
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate =new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
}
if (md.isIgnored()) {
result.put(md.configKey(), args -> {
throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
});
} else {
result.put(md.configKey(),
//SynchronousMethodHandler
factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
}
}
return result;
}
这里面主要就是使用contract去解析那些注解,然后把结果保存到 List里面,然后把list转化成map,其中map的key:
public static String configKey(Class targetType, Method method) {
StringBuilder builder = new StringBuilder();
builder.append(targetType.getSimpleName());
builder.append('#').append(method.getName()).append('(');
for (Type param : method.getGenericParameterTypes()) {
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();
}
说白了,就是把接口里面的每一个方法解析成一个key-value对,其中key是类名#方法名(参数类列表),value是SynchronousMethodHandler。
看下ReflectiveFeign.FeignInvocationHandler的invoke:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
return dispatch.get(method).invoke(args);
}
equals、hashCode、toString直接就处理掉了,其他的方法往下走:
dispatch.get(method)拿到的是SynchronousMethodHandler,看下它的invoke:
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
这里还做做了重试,逻辑就不看了,看下executeAndDecode:
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
//根据RequestTemplate构造出Request
Request request = targetRequest(template);
Response response;
long start = System.nanoTime();
try { //执行request,得到response
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 12
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
//解码response
if (decoder != null)
return decoder.decode(response, metadata.returnType());
//如果没有定义解码器,异步解析响应
CompletableFuture<Object> resultFuture = new CompletableFuture<>();
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,metadata.returnType(),elapsedTime);
try {
if (!resultFuture.isDone())
throw new IllegalStateException("Response handling not done");
//等待执行结果
return resultFuture.join();
} catch (CompletionException e) {
Throwable cause = e.getCause();
if (cause != null)
throw cause;
throw e;
}
}
}
这个是核心的处理。
整个源码的扩展性还是非常好的,只要在builder里面替换掉相应的组件就可以了,后面我们会来看下springcloud里面是如何替换这里面的组件的。
欢迎扫码查看更多文章: