24.调用链如何构建的?ProtocolFilterWrapper分析
调用链的构建,可以理解成多个invoker实例组成链的构建。
调用链构建在dubbo中分为两步骤:
- JavassistProxyFactory$1的构建。此步骤是将真正的业务方法同规避反射的手段包起来。
- ProtocolFilterWrapper$1的构建。此步骤是将每个filter用Invoker实例包起来并串起来链来。
具体代码和示意图参见下面
代码
JavassistProxyFactory$1的构建代码
// JavassistProxyFactory
// public class JavassistProxyFactory extends AbstractProxyFactory
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper类不能正确处理带$的类名
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
ProtocolFilterWrapper$1的构建代码
// ProtocolFilterWrapper
// public class ProtocolFilterWrapper implements Protocol
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (filters.size() > 0) {
for (int i = filters.size() - 1; i >= 0; i --) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
public Class<T> getInterface() {
return invoker.getInterface();
}
public URL getUrl() {
return invoker.getUrl();
}
public boolean isAvailable() {
return invoker.isAvailable();
}
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
构建图示
ProtocolFilterWrapper$1的构建图示
filter * n
+-+-+ +-+-+
① | +----------+ +----------+ +----------+ +----------+ |
| | filter0 <----|filter1 <---+ filter2 <---|filtern | |
| +----------+ +----------+ +----------+ +----------+ |
+-+-+ +-+-+
<-------------------------+
+----------------------------------------+ ② |
③ JavassistProxyFactory$1 | |
| |
+ + | |
+--------+ inVoker | | |
| +--------------------v----+ |
| | filtern | next | |
| +-------------+-----------+ |
| |
+-----------------------------+ |
| |
build + + | |
Invoker +--------+ inVoker | | |
Chain | +--------------------v----+ |
| | filter2 | next | |
| +-------------+-----------+ |
| |
+-----------------------------+ |
| |
+ + | |
+--------+ inVoker | | |
| +--------------------v----+ |
| | filter1 | next | |
| +-------------+-----------+ |
| |
+-----------------------------+ |
| |
+ + | |
+--------+ inVoker | | |
| +--------------------v----+ |
| | filter0 | next | v
| +-------------+-----------+
|
|
<----------+
④ ProtocolFilterWrapper$1
解释
JavassistProxyFactory$1的构建解释
此步骤是将真正的业务方法同规避反射的手段包起来。具体实现逻辑在:
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
这个通过动态生成生成的Wrapper通过直接调用业务类方法规避了反射调用。具体细节和生成的代码可以参见《反射调用是如何优化的?Wrapper的作用是什么?》。
ProtocolFilterWrapper$1的构建解释
从③处将JavassistProxyFactory$1送进来到④处将ProtocolFilterWrapper$1送出去之间,就是构建调用链的整个过程。
构建过程中倒序迭代过滤器,将每一个过滤器用invoker匿名内部类包起来,并结合外围变量next组成一个类似闭包的样子,通过每次切换外围next变量的指向形成链。迭代过程中,每次next都指向上一次构建invoker匿名内部类包起来的闭包。
为啥要倒序迭代?因为这样能达成调用时正序迭代,结合调用时调用栈能看的更清楚。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!