服务导出的入口方法是 ServiceBean 的 onApplicationEvent。onApplicationEvent 是一个事件响应方法,该方法会在收到 Spring 上下文刷新事件后执行服务导出操作。方法代码如下:
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
private void doExportUrls() {
// 拼装url
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
ApplicationModel.initProviderModel(pathKey, providerModel);
// 根据协议进行导出服务
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
protected List<URL> loadRegistries(boolean provider) {
// check && override if necessary
List<URL> registryList = new ArrayList<URL>();
if (CollectionUtils.isNotEmpty(registries)) {
for (RegistryConfig config : registries) {
String address = config.getAddress();
if (StringUtils.isEmpty(address)) {
address = ANYHOST_VALUE;
}
if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map<String, String> map = new HashMap<String, String>();
appendParameters(map, application);
appendParameters(map, config);
map.put(PATH_KEY, RegistryService.class.getName());
appendRuntimeParameters(map);
if (!map.containsKey(PROTOCOL_KEY)) {
map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
}
List<URL> urls = UrlUtils.parseURLs(address, map);
for (URL url : urls) {
url = URLBuilder.from(url)
.addParameter(REGISTRY_KEY, url.getProtocol())
.setProtocol(REGISTRY_PROTOCOL)
.build();
if ((provider && url.getParameter(REGISTER_KEY, true))
|| (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
registryList.add(url);
}
}
}
}
}
return registryList;
}
循环遍历 registries,获取url,并且拼装参数,获取到的 registryURLs 如下
string =
“registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=tea-life-provider&check=true&dubbo=2.0.2&pid=7057®istry=zookeeper&release=2.7.3×tamp=1572159342766&version=1.0”
String name = protocolConfig.getName();
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, PROVIDER_SIDE);
appendRuntimeParameters(map);
appendParameters(map, metrics);
appendParameters(map, application);
appendParameters(map, module);
// remove 'default.' prefix for configs from ProviderConfig
// appendParameters(map, provider, Constants.DEFAULT_KEY);
appendParameters(map, provider);
appendParameters(map, protocolConfig);
appendParameters(map, this);
解析 metricsConfig、applicationConfig、moduleConfig、providerConfig、protocolConfig、serviceConfig,将解析到的数据放入map
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
解析 接口中的方法,将其放入map
if (!ConfigUtils.isEmpty(token)) {
if (ConfigUtils.isDefault(token)) {
map.put(TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(TOKEN_KEY, token);
}
}
随机生成token,将其放入map
// export service
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = this.findConfigedPorts(protocolConfig, name, map);
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
String scope = url.getParameter(SCOPE_KEY);
// don't export when none is configured
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// export to local if the config is not remote (export to remote only when config is remote)
// 导出本地服务
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
// export to remote if the config is not local (export to local only when config is local)
// 导出远程服务
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
}
Dubbo 默认的 ProxyFactory 实现类是 JavassistProxyFactory
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
// 创建包装类,解析接口中的信息
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
// doInvoke的时候,其实就是调用接口中方法的过程
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);
}
};
}
个人觉得这个类还是很重要的,Wrapper.getWrapper(Clazz.class)的时候会创建Wapper,将接口的信息解析,存放在
Map
WRAPPER_MAP 对应的是 Class 与 Wraper的关系,Wraper中包含接口的方法,属性等
public abstract class Wrapper {
public static Wrapper getWrapper(Class<?> c) {
while(ClassGenerator.isDynamicClass(c)) {
c = c.getSuperclass();
}
if (c == Object.class) {
return OBJECT_WRAPPER;
} else {
Wrapper ret = (Wrapper)WRAPPER_MAP.get(c);
if (ret == null) {
ret = makeWrapper(c);
WRAPPER_MAP.put(c, ret);
}
return ret;
}
}
// Wrapper 中包含着方法,属性,还可以invokeMethod()
private static final Wrapper OBJECT_WRAPPER = new Wrapper() {
public String[] getMethodNames() {
return Wrapper.OBJECT_METHODS;
}
public String[] getDeclaredMethodNames() {
return Wrapper.OBJECT_METHODS;
}
public String[] getPropertyNames() {
return Wrapper.EMPTY_STRING_ARRAY;
}
public Class<?> getPropertyType(String pn) {
return null;
}
public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException {
throw new NoSuchPropertyException("Property [" + pn + "] not found.");
}
public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException {
throw new NoSuchPropertyException("Property [" + pn + "] not found.");
}
public boolean hasProperty(String name) {
return false;
}
public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException {
if ("getClass".equals(mn)) {
return instance.getClass();
} else if ("hashCode".equals(mn)) {
return instance.hashCode();
} else if ("toString".equals(mn)) {
return instance.toString();
} else if ("equals".equals(mn)) {
if (args.length == 1) {
return instance.equals(args[0]);
} else {
throw new IllegalArgumentException("Invoke method [" + mn + "] argument number error.");
}
} else {
throw new NoSuchMethodException("Method [" + mn + "] not found.");
}
}
};
其实最后还是调用 Wrapper.invokeMethod()执行
// export to remote if the config is not local (export to local only when config is local)
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
if (!isOnlyInJvm() && logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
//if protocol is only injvm ,not register
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
}
// For providers, this is used to enable custom proxy to generate invoker
String proxy = url.getParameter(PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(PROXY_KEY, proxy);
}
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 这里进行的是远程服务的导出,注册
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
/**
* @since 2.7.0
* ServiceData Store
*/
MetadataReportService metadataReportService = null;
if ((metadataReportService = getMetadataReportService()) != null) {
metadataReportService.publishProvider(url);
}
}
export方法其实干了两件事:
1、将上一步拿到的Invoker export出去,然后创建 DubboExporter实例并存储在 exporterMap 里,Exporter并没有什么玄机,就是一个存储了Invoker实例及其他各种信息的容器,用于之后获取Invoker用。
2、打开服务端口,并注册ExchangeHandler,用于后续响应客户端网络请求,响应逻辑这里先不展开。