ServiceBean是暴露服务的入口
ServiceBean
- 看起来貌似很复杂的样子,其实都是纸老虎。我们分为将上述类分为如下几类
- XxxAware:实现该接口的Bean均可以通过SetXxx方法,注入Xxx对象
- XxxConfig:服务暴露方暴露服务需要的相关配置;这个继承关系也很有意思,类似 属性->方法->接口->服务的配置
- ApplicationListener:上下文监听器,可以监听上下文的相关事件,ServiceBean关心的是ContextRefreshedEvent事件
- XxxBean:走过Bean生命周期的接口
对于服务的暴露我们其实只需要关心两个接口InitializingBean和ApplicationListener
public interface InitializingBean {
//在bean初始化后调用该方法
void afterPropertiesSet() throws Exception;
}
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
//上下文触发不同类型的事件,调用该方法
void onApplicationEvent(E var1);
}
afterPropertiesSet()
public void afterPropertiesSet() throws Exception {
//没给ServiceBean配置Provider属性,从applicationContext获取类型为ProviderConfig(<dubbo:provider)的bean
//获取到了进行配置
if (getProvider() == null) {
Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
if (providerConfigMap != null && providerConfigMap.size() > 0) {
Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
&& providerConfigMap.size() > 1) { // backward compatibility
List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
for (ProviderConfig config : providerConfigMap.values()) {
if (config.isDefault() != null && config.isDefault().booleanValue()) {
providerConfigs.add(config);
}
}
if (!providerConfigs.isEmpty()) {
setProviders(providerConfigs);
}
} else {
ProviderConfig providerConfig = null;
for (ProviderConfig config : providerConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (providerConfig != null) {
throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
}
providerConfig = config;
}
}
if (providerConfig != null) {
setProvider(providerConfig);
}
}
}
}
//ApplicationConfug配置赋值
if (getApplication() == null
&& (getProvider() == null || getProvider().getApplication() == null)) {
Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
/**
* applicationConfig只能配置一个
*/
ApplicationConfig applicationConfig = null;
for (ApplicationConfig config : applicationConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (applicationConfig != null) {
throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
}
applicationConfig = config;
}
}
if (applicationConfig != null) {
setApplication(applicationConfig);
}
}
}
//ModuleConfig赋值
if (getModule() == null
&& (getProvider() == null || getProvider().getModule() == null)) {
Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
ModuleConfig moduleConfig = null;
for (ModuleConfig config : moduleConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (moduleConfig != null) {
throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
}
moduleConfig = config;
}
}
if (moduleConfig != null) {
setModule(moduleConfig);
}
}
}
//注册中心配置赋值
if ((getRegistries() == null || getRegistries().isEmpty())
&& (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().isEmpty())
&& (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().isEmpty())) {
Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
if (registryConfigMap != null && registryConfigMap.size() > 0) {
List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
for (RegistryConfig config : registryConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
registryConfigs.add(config);
}
}
if (registryConfigs != null && !registryConfigs.isEmpty()) {
super.setRegistries(registryConfigs);
}
}
}
//监控中心MonitorConfig配置赋值
if (getMonitor() == null
&& (getProvider() == null || getProvider().getMonitor() == null)
&& (getApplication() == null || getApplication().getMonitor() == null)) {
Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
MonitorConfig monitorConfig = null;
for (MonitorConfig config : monitorConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (monitorConfig != null) {
throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
}
monitorConfig = config;
}
}
if (monitorConfig != null) {
setMonitor(monitorConfig);
}
}
}
//协议ProtocolConfig配置赋值
if ((getProtocols() == null || getProtocols().isEmpty())
&& (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().isEmpty())) {
Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
for (ProtocolConfig config : protocolConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
protocolConfigs.add(config);
}
}
if (protocolConfigs != null && !protocolConfigs.isEmpty()) {
super.setProtocols(protocolConfigs);
}
}
}
//设置path
if (getPath() == null || getPath().length() == 0) {
if (beanName != null && beanName.length() > 0
&& getInterface() != null && getInterface().length() > 0
&& beanName.startsWith(getInterface())) {
setPath(beanName);
}
}
//如果不是延迟暴露服务,则调用export暴露服务
//否则会通过{@link onApplicationEvent}方法延迟暴露服务
if (!isDelay()) {
export();
}
}
/**
* true: 延迟导出服务
* false: 无需延迟导出服务
*/
private boolean isDelay() {
Integer delay = getDelay();
ProviderConfig provider = getProvider();
if (delay == null && provider != null) {
delay = provider.getDelay();
}
return supportedApplicationListener && (delay == null || delay == -1);
}
该方法主要是判断咱们有没有通过类似<dubbo:service interface="com.alibaba.dubbo.study.day01.xml.service.EchoService" ref="echoService"/>配置提供ProviderConfig、ApplicationConfig、ModuleConfig、RegistryConfig(多个)、MonitorConfig、ProtocolConfig(多个)配置;没有的话通过在Spring的上下文容器中查找这些配置,并设置到ServiceBean的属性里。最后根据isDelay()方法判断是否需要暴露服务。
onApplicationEvent(ContextRefreshedEvent event)
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//延迟暴露,未导出过,没有取消导出
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
如果通过afterPropertiesSet()方法未导出的服务,会通过该方法最终进行调用export()方法进行服务导出
export()
public synchronized void export() {
if (provider != null) {
// 获取 export 和 delay 配置
if (export == null) {
export = provider.getExport();
}
if (delay == null) {
delay = provider.getDelay();
}
}
// 如果 export 为 false,则不导出服务
if (export != null && !export) {
return;
}
//delay>0延迟导出服务
if (delay != null && delay > 0) {
delayExportExecutor.schedule(new Runnable() {
@Override
public void run() {
doExport();
}
}, delay, TimeUnit.MILLISECONDS);
} else {
//即可导出服务
doExport();
}
}
主要是根据配置判断是否导出服务和延迟导出服务的处理;需要注意的是延迟导出使用了delayExportExecutor线程池进行服务的导出
doExport()
protected synchronized void doExport() {
//已取消导出判断
if (unexported) {
throw new IllegalStateException("Already unexported!");
}
//导出过了判断
if (exported) {
return;
}
exported = true;
//interfaceName参数校验
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}
//如果provider为空,为空则新建一个,并通过系统变量为其初始化
checkDefault();
/**
* registries,monitor的优先级配置ProviderConfig>ModuleConfig>ApplicationConfig>
*/
if (provider != null) {
if (application == null) {
application = provider.getApplication();
}
if (module == null) {
module = provider.getModule();
}
if (registries == null) {
registries = provider.getRegistries();
}
//MonitorConfig
if (monitor == null) {
monitor = provider.getMonitor();
}
//List<ProtocolConifg>
if (protocols == null) {
protocols = provider.getProtocols();
}
}
if (module != null) {
if (registries == null) {
registries = module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
if (application != null) {
if (registries == null) {
registries = application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
//ref为泛化调用类型
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
//设置generic=true
generic = Boolean.TRUE.toString();
}
} else {
//加载interfaceName对应的Class
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
//检查给定接口和接口的方法配置尅合法
checkInterfaceAndMethods(interfaceClass, methods);
//检查ref指定的对象是否为interfaceClass指定的类型实例
checkRef();
// 设置 generic = "false"
generic = Boolean.FALSE.toString();
}
//local属性不为null
if (local != null) {
if ("true".equals(local)) {
local = interfaceName + "Local";
}
Class<?> localClass;
try {
//加载本地存根类
localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
}
}
if (stub != null) {
if ("true".equals(stub)) {
stub = interfaceName + "Stub";
}
Class<?> stubClass;
try {
stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
}
}
//检查并配置ApplicationConfg
checkApplication();
//检查并配置RegistryConfig
checkRegistry();
//检查并配置ProtocolConfig
checkProtocol();
//为了向后兼容ServiceConfig的配置
appendProperties(this);
//检测存根类
checkStub(interfaceClass);
//检测mock属性是否合法
checkMock(interfaceClass);
if (path == null || path.length() == 0) {
path = interfaceName;
}
//导出服务
doExportUrls();
/**
* 服务提供者模型
* 比如服务的配置信息,服务实例等,
* 每个被导出的服务对应一个 ProviderModel。
* ApplicationModel 持有所有的 ProviderModel。
*/
ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
//将providerModel保存到Applicationmodel维护的一个map中
ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
}
- 1.首先服务导出状态判断
- 2.checkDefault()方法检查ProviderConfig是否配置,没配置通过系统变量(System.getProperty()或者指定目录下的配置文件)初始化
- 3.检测 ProviderConfig、ApplicationConfig 等核心配置类对象是否为空,若为空,则尝试从其他配置类对象中获取相应的实例。
- 4.本地存根验证
- 5.对ApplicationConfig、RegistryConfig、ProtocolConfig、ServiceConfig等配置的进一步初始化验证
- 6.进入导出逻辑的关键部分doExportUrls()方法
- 7.封装一个提供者模型对象ProviderModel对象,并加入缓存,各位同学可以通过ApplicationModel的相关get方法获得ProviderModel,感兴趣的可以研究下吧。
appendProperties(AbstractConfig config):通过系统环境变量配置Config,主要目的是为了兼容1.0版本吧
protected static void appendProperties(AbstractConfig config) {
if (config == null) {
return;
}
//前缀为dubbo.provider.
String prefix = "dubbo." + getTagName(config.getClass()) + ".";
//获取配置类的所有方法
Method[] methods = config.getClass().getMethods();
for (Method method : methods) {
try {
String name = method.getName();
//set方法、public的,只有一个参数,参数必须为基本类型
if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(method.getModifiers())
&& method.getParameterTypes().length == 1 && isPrimitive(method.getParameterTypes()[0])) {
//属性名称比如host path
String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), ".");
String value = null;
//比如dubbo.provider.id.host
if (config.getId() != null && config.getId().length() > 0) {
//dubbo.provider.id.host
String pn = prefix + config.getId() + "." + property;
value = System.getProperty(pn);
if (!StringUtils.isBlank(value)) {
logger.info("Use System Property " + pn + " to config dubbo");
}
}
//比如dubbo.provider.host
if (value == null || value.length() == 0) {
String pn = prefix + property;
value = System.getProperty(pn);
if (!StringUtils.isBlank(value)) {
logger.info("Use System Property " + pn + " to config dubbo");
}
}
if (value == null || value.length() == 0) {
//属性的get或者is方法
Method getter;
try {
getter = config.getClass().getMethod("get" + name.substring(3));
} catch (NoSuchMethodException e) {
try {
getter = config.getClass().getMethod("is" + name.substring(3));
} catch (NoSuchMethodException e2) {
getter = null;
}
}
if (getter != null) {
//配置类的getter方法返回null,从系统环境变量dubbo.properties.file指定的文件中读取值
if (getter.invoke(config) == null) {
if (config.getId() != null && config.getId().length() > 0) {
value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);
}
if (value == null || value.length() == 0) {
value = ConfigUtils.getProperty(prefix + property);
}
if (value == null || value.length() == 0) {
String legacyKey = legacyProperties.get(prefix + property);
if (legacyKey != null && legacyKey.length() > 0) {
value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));
}
}
}
}
}
if (value != null && value.length() > 0) {
method.invoke(config, convertPrimitive(method.getParameterTypes()[0], value));
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
doExportUrls()
private void doExportUrls() {
//加载注册中心URL
List<URL> registryURLs = loadRegistries(true);
//按照不同的协议导出url
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
上面代码首先是通过 loadRegistries 加载注册中心链接,然后再遍历 ProtocolConfig 集合导出每个服务。并在导出服务的过程中,将服务注册到注册中心。下面,我们先来看一下 loadRegistries 方法的逻辑。
loadRegistries(boolean provider)
protected List<URL> loadRegistries(boolean provider) {
//检测注册中心配置
checkRegistry();
List<URL> registryList = new ArrayList<URL>();
if (registries != null && !registries.isEmpty()) {
//遍历所有注册中心
for (RegistryConfig config : registries) {
//注册中心的地址
String address = config.getAddress();
//若 address 为空,则将其设为 0.0.0.0
if (address == null || address.length() == 0) {
address = Constants.ANYHOST_VALUE;
}
//从系统属性中加载注册中心地址
String sysaddress = System.getProperty("dubbo.registry.address");
if (sysaddress != null && sysaddress.length() > 0) {
address = sysaddress;
}
//注册中心地址不是N/A
if (address.length() > 0 && !RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map<String, String> map = new HashMap<String, String>();
// 添加 ApplicationConfig 中的字段信息到 map 中
appendParameters(map, application);
// 添加 RegistryConfig 字段信息到 map 中
appendParameters(map, config);
//添加path dubbo timestamp pid protocol属性到map中
map.put("path", RegistryService.class.getName());
map.put("dubbo", Version.getProtocolVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
}
//如果还没有定义protocol
if (!map.containsKey("protocol")) {
if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) {
map.put("protocol", "remote");
} else {
map.put("protocol", "dubbo");
}
}
//解析得到一个URL列表,这是因为address可能是";"分隔的多个地址
//zookeeper://192.168.1.233:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-provider&dubbo=2.0.2&pid=6696×tamp=1571986235561
List<URL> urls = UrlUtils.parseURLs(address, map);
for (URL url : urls) {
url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
//将URL协议头设置为registry
url = url.setProtocol(Constants.REGISTRY_PROTOCOL);//registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-provider&dubbo=2.0.2&pid=14308®istry=zookeeper×tamp=1571986326534
//判断url是否添加到url registryList中
//(服务提供者 && register = true 或 null)
// || (非服务提供者 && subscribe = true 或 null)
if ((provider && url.getParameter(Constants.REGISTER_KEY, true))
|| (!provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) {
registryList.add(url);
}
}
}
}
}
return registryList;
}
1.检测注册中心配置
2.address的处理
3.将ApplicationConfig、RegistryConfig配置的字段属性信息放入map中;
4.添加path dubbo timestamp pid protocol等属性到map中
5.根据注册中心的address配置地址(可能有多个)和map转换为URL的集合(注册中心链接)
6.遍历链接列表,并根据条件决定是否将其添加到 registryList 中
appendParameters(map,application):用于将配置类的属性加入到map
protected static void appendParameters(Map<String, String> parameters, Object config, String prefix) {
if (config == null) {
return;
}
Method[] methods = config.getClass().getMethods();
for (Method method : methods) {
try {
String name = method.getName();
//方法名以get或者is开头
//不是getClass方法
//public修饰的方法
//返回值类型为一些节本类型的类型
if ((name.startsWith("get") || name.startsWith("is"))
&& !"getClass".equals(name)
&& Modifier.isPublic(method.getModifiers())
&& method.getParameterTypes().length == 0
&& isPrimitive(method.getReturnType())) {
//获取方法的Parameter注解
//如果方法
Parameter parameter = method.getAnnotation(Parameter.class);
//方法返回Object类型或者(parameter注解不是null并且方法excluded)
if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
continue;
}
//get或者is方法的属性名称对应的下标号
//getXxx返回3 isXxx返回2
int i = name.startsWith("get") ? 3 : 2;
//属性名称
String prop = StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), ".");
String key;
//通过@Parameter注解指定key
if (parameter != null && parameter.key().length() > 0) {
key = parameter.key();
} else {
key = prop;
}
//反射获取调用值
Object value = method.invoke(config);
//字符串化getXxx的返回值
String str = String.valueOf(value).trim();
//如果反射调用返回值存在;并且需要根据@Parameter做一些特殊处理
if (value != null && str.length() > 0) {
//指定需要编码
if (parameter != null && parameter.escaped()) {
str = URL.encode(str);
}
if (parameter != null && parameter.append()) {
String pre = parameters.get(Constants.DEFAULT_KEY + "." + key);
if (pre != null && pre.length() > 0) {
str = pre + "," + str;
}
pre = parameters.get(key);
if (pre != null && pre.length() > 0) {
str = pre + "," + str;
}
}
//是否包含prefix参数
if (prefix != null && prefix.length() > 0) {
key = prefix + "." + key;
}
parameters.put(key, str);
} else if (parameter != null && parameter.required()) {
throw new IllegalStateException(config.getClass().getSimpleName() + "." + key + " == null");
}
//如果是getParameters方法
} else if ("getParameters".equals(name)
&& Modifier.isPublic(method.getModifiers())
&& method.getParameterTypes().length == 0
&& method.getReturnType() == Map.class) {
//反射调用返回getParameters的返回值
Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);
if (map != null && map.size() > 0) {
//parameters map中key的前缀
String pre = (prefix != null && prefix.length() > 0 ? prefix + "." : "");
for (Map.Entry<String, String> entry : map.entrySet()) {
parameters.put(pre + entry.getKey().replace('-', '.'), entry.getValue());
}
}
}
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
在加入到map的过程中受到@Parameter的方法影响:比如excluded()方法为true代表该属性不放到map,key()方法返回了一个用于代替属性的键值,escaped()方法对值进行编码。
doExportUrlsFor1Protocol(protocolConfig,RegistryURLs)
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
//协议默认设置为dubbo
String name = protocolConfig.getName();
if (name == null || name.length() == 0) {
name = "dubbo";
}
Map<String, String> map = new HashMap<String, String>();
//添加side dubbo版本 时间戳 进程号信息到map中
map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
}
//添加ApplicationConfig ModuleConfig ProviderConig ProtocolConfig ServiceConfig
//信息到Map中
appendParameters(map, application);
appendParameters(map, module);
appendParameters(map, provider, Constants.DEFAULT_KEY);
appendParameters(map, protocolConfig);
appendParameters(map, this);
//methods存储了通过解析<dubbo:method标签配置的信息和<dubbo:argument配置的信息
/**
* <dubbo:method name="addListener" retries="2">
* <!--<dubbo:argument index="1" callback="true"/>-->
* <!--也可以通过指定类型的方式-->
* <dubbo:argument type="com.alibaba.dubbo.study.day03.callback.CallbackListener" callback="true" />
* </dubbo:method>
* 解析这种配置并存入到map中
* addListener.retries=2
* addListener.1.callback=true
*/
if (methods != null && !methods.isEmpty()) {
for (MethodConfig method : methods) {
appendParameters(map, method, method.getName());
// 添加 MethodConfig 对象的字段信息到 map 中,键 = 方法名.属性名。
// 比如存储 <dubbo:method name="sayHello" retries="2"> 对应的 MethodConfig,
// 键 = sayHello.retries,map = {"sayHello.retries": 2, "xxx": "yyy"}
String retryKey = method.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
// 检测 MethodConfig retry 是否为 false,若是,则设置重试次数为0
if ("false".equals(retryValue)) {
map.put(method.getName() + ".retries", "0");
}
}
//获取method对应ArgumentConfig
List<ArgumentConfig> arguments = method.getArguments();
if (arguments != null && !arguments.isEmpty()) {
//循环ArgumentConfig
for (ArgumentConfig argument : arguments) {
// convert argument type
//如果存在type配置
if (argument.getType() != null && argument.getType().length() > 0) {
Method[] methods = interfaceClass.getMethods();
// 接口的所有方法
if (methods != null && methods.length > 0) {
for (int i = 0; i < methods.length; i++) {
String methodName = methods[i].getName();
// target the method, and get its signature
// 比对方法名,查找目标方法
if (methodName.equals(method.getName())) {
//方法的类型数组
Class<?>[] argtypes = methods[i].getParameterTypes();
// one callback in the method
//如果参数下标不为-1
if (argument.getIndex() != -1) {
// 添加 ArgumentConfig 字段信息到 map 中,
// 键前缀 = 方法名.index,比如:
// map = {"sayHello.3": true}
if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
appendParameters(map, argument, method.getName() + "." + argument.getIndex());
} else {
throw new IllegalArgumentException("argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
}
} else {
// multiple callbacks in the method
for (int j = 0; j < argtypes.length; j++) {
Class<?> argclazz = argtypes[j];
// 从参数类型列表中查找类型名称为 argument.type 的参数
if (argclazz.getName().equals(argument.getType())) {
appendParameters(map, argument, method.getName() + "." + j);
if (argument.getIndex() != -1 && argument.getIndex() != j) {
throw new IllegalArgumentException("argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
}
}
}
}
}
}
}
//用户未配置type但是配置了index 不为-1
} else if (argument.getIndex() != -1) {
appendParameters(map, argument, method.getName() + "." + argument.getIndex());
} else {
throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
}
}
}
} // end of methods for
}
//是否为泛化调用
if (ProtocolUtils.isGeneric(generic)) {
map.put(Constants.GENERIC_KEY, generic);
map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);
} else {
//获得版本号
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put("revision", revision);
}
//为接口生成包裹类 Wrapper,Wrapper 中包含了接口的详细信息,比如接口方法名数组,字段信息等
获取包装类,包装类实质上就是通过javassist生成的类,当我们通过包装类对角调dubbo接口时实际调的还是接口对象的原有方法。
// 此处包装起到的作用无非是可以以统一的代码的去调用用户提供的不同的dubbo接口
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("NO method found in service interface " + interfaceClass.getName());
map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);
} else {
// 添加方法名到 map 中,如果包含多个方法名,则用逗号隔开,比如 method = init,destroy
map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
//将token添加到map中
if (!ConfigUtils.isEmpty(token)) {
// 随机生成 token
if (ConfigUtils.isDefault(token)) {
map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(Constants.TOKEN_KEY, token);
}
}
//判断协议名称是否为injvm
if (Constants.LOCAL_PROTOCOL.equals(protocolConfig.getName())) {
//注册到注册中心参数设置为false
protocolConfig.setRegister(false);
map.put("notify", "false");
}
// export service
// 获取protocolConfig的contextPath没有的话使用provider的contextPath
String contextPath = protocolConfig.getContextpath();
if ((contextPath == null || contextPath.length() == 0) && provider != null) {
contextPath = provider.getContextpath();
}
// 获取 host 和 port
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = this.findConfigedPorts(protocolConfig, name, map);
//组装url emmm
//dubbo://169.254.22.149:20880/com.alibaba.dubbo.study.day01.xml.service.EchoService?
// addListener.1.callback=true&addListener.retries=2&anyhost=true&application=echo-provider
// &bean.name=com.alibaba.dubbo.study.day01.xml.service.EchoService&bind.ip=169.254.22.149&bind.port=20880
// &dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService
// &methods=echo,addListener&pid=4456&side=provider×tamp=1572244285807
URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
//spi 判断是否有url.getProtocol
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
// 通过Configurator配置url
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
//获取scope属性,scope属性决定服务是否暴露本地、不暴露、远程
String scope = url.getParameter(Constants.SCOPE_KEY);
//当scope=none 什么都不做
if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
// export to local if the config is not remote (export to remote only when config is remote)
// scope!=remote,服务暴露在本地
if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
exportLocal(url);
}
// export to remote if the config is not local (export to local only when config is local)
// scope!=local导出到远程上嘞
if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
//如果注册中心URL地址不为Null
if (registryURLs != null && !registryURLs.isEmpty()) {
for (URL registryURL : registryURLs) {
url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
//加载监视器连接
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
//给url添加key=monitoUrl
//添加监视器配置
url = url.addParameterAndEncoded(Constants.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
// 获取proxy属性,创建代理的方式javaassit或者jdkproxy
String proxy = url.getParameter(Constants.PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
}
//为服务提供类(refer)生成Invoker
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
//使用DelegateProviderMetaDataInvoker封装生成的invoker和当前serviceCOnfig
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
//导出服务,并生成 Exporter
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
//如果不存在注册中心,导出服务
} else {
//为服务生成invoker类
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
//使用DelegateProviderMetaDataInvoker封装生成的invoker和当前serviceCOnfig
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
//导出服务,并生成 Exporter
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
}
}
this.urls.add(url);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void exportLocal(URL url) {
// 如果协议不是injvm
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
// 生成本地的url,分别把协议改为injvm,设置host和port
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL)
.setHost(LOCALHOST)
.setPort(0);
// 通过代理工厂创建invoker
// 再调用export方法进行暴露服务,生成Exporter
StaticContext.getContext(Constants.SERVICE_IMPL_CLASS).put(url.getServiceKey(), getServiceClass(ref));
Exporter<?> exporter = protocol.export(
proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
//加入到exproters
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
}
}
- 1.首先版本、时间戳、方法名以及各种配置对象的字段信息放入到 map 中
- 2.然后ApplicationConfig、ModuleConfig、ProviderConfig、ProtocolConfig、ServiceConfig配置的信息放到map
- 3.解析<dubbo:method和<dubbo:argument配置如
<dubbo:service interface="com.alibaba.dubbo.study.day01.xml.service.EchoService" ref="echoService">
<dubbo:method name="addListener" retries="2">
<!--<dubbo:argument index="1" callback="true"/>-->
<!--也可以通过指定类型的方式-->
<dubbo:argument type="com.alibaba.dubbo.study.day03.callback.CallbackListener" callback="true" />
</dubbo:method>
</dubbo:service>
- 放到map里的信息(addListener.retries=2、addListener.1.callback=true)
- 4.放入泛化调用信息,版本号,各个方法名称,token数据,导出本地到map
- 5.获取host,port,然后根据map里的数据创建URL
- 6.根据url中的scope属性导出服务