一、目标
二、原理
远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。通过代理类去完成这个调用,这样在 Stub类中,就可以做一些额外的事,来对服务的调用过程进行优化或者容错的处理。
三、示例
四、源码
服务引用初始化时,进行本地伪装合法性校验。
void checkStubAndLocal(Class<?> interfaceClass) {
if (ConfigUtils.isNotEmpty(local)) {
Class<?> localClass = ConfigUtils.isDefault(local) ?
ReflectUtils.forName(interfaceClass.getName() + "Local") : ReflectUtils.forName(local);
verify(interfaceClass, localClass);
}
if (ConfigUtils.isNotEmpty(stub)) {
//加载Stub类是否存在
Class<?> localClass = ConfigUtils.isDefault(stub) ?
ReflectUtils.forName(interfaceClass.getName() + "Stub") : ReflectUtils.forName(stub);
//校验Stub类是否合法
verify(interfaceClass, localClass);
}
}
创建服务引用代理。
private T createProxy(Map<String, String> map) {
//忽略部分代码....
//javaassist生成动态代理类
return (T) PROXY_FACTORY.getProxy(invoker);
}
根据 Dubbo SPI 扩展机制,会先使用 ProxyFactory 的包装类,对 ProxyFactory 进行了功能增强,增强了本地存根功能,这类似与AOP。
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
//创建服务引用的目标代理
T proxy = proxyFactory.getProxy(invoker);
//判断是否泛型服务类型
if (GenericService.class != invoker.getInterface()) {
URL url = invoker.getUrl();
String stub = url.getParameter(STUB_KEY, url.getParameter(LOCAL_KEY));
if (ConfigUtils.isNotEmpty(stub)) {
Class<?> serviceType = invoker.getInterface();
if (ConfigUtils.isDefault(stub)) {
if (url.hasParameter(STUB_KEY)) {
stub = serviceType.getName() + "Stub";
} else {
stub = serviceType.getName() + "Local";
}
}
try {
//加载、判断本地存根的类型是否存在
Class<?> stubClass = ReflectUtils.forName(stub);
if (!serviceType.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + serviceType.getName());
}
try {
//反射,目标代理作为构造参数,生成本地存根实例
Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);
proxy = (T) constructor.newInstance(new Object[]{proxy});
//export stub service
URLBuilder urlBuilder = URLBuilder.from(url);
if (url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT)) {
urlBuilder.addParameter(STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
urlBuilder.addParameter(IS_SERVER_KEY, Boolean.FALSE.toString());
try {
export(proxy, (Class) invoker.getInterface(), urlBuilder.build());
} catch (Exception e) {
LOGGER.error("export a stub service error.", e);
}
}
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implementation class " + stubClass.getName(), e);
}
} catch (Throwable t) {
LOGGER.error("Failed to create stub implementation class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
// ignore
}
}
}
return proxy;
}
引用