昨天我们已经知道dubbo的入口可以通过自定义标签找到DubboNamespaceHandler找到解析自定义标签的相关类了。还有4个我们需要关注:ConfigCenterBean、ServiceBean、ReferenceBean、new AnnotationBeanDefintionParser()。
下面我们来逐一了解:
1.ConfigCenterBean
从继承关系来看,我们知道ConfigCenterBean继承ConfigCenterConfig,同时实现了三个接口:ApplicationContextAware、EnvironmentAware、DisposableBean。而我们知道实现Aware接口的Bean在被初始化之后,可以拿到一些对应的资源。也就是实现ApplicationContextAware、EnviromentAware会在bean初始化之后,会被注入到ApplicationContext和Enviroment中。而DisposableBean则是进行Bean销毁的扩展入口,方便在销毁之前做一些个性化的操作。因此我们在里面可以重写destory方法,同时对ApplicationContext和Enviroment进行了set和get:
ConfigCenterBean
代码语言:javascript复制//配置中心Bean
public class ConfigCenterBean extends ConfigCenterConfig implements ApplicationContextAware, DisposableBean, EnvironmentAware {
//上下文信息
private transient ApplicationContext applicationContext;
private Boolean includeSpringEnv = false;
//设置上下文,同将上下文添加到Spring扩展工厂中
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
SpringExtensionFactory.addApplicationContext(applicationContext);
}
//复写销毁方法
@Override
public void destroy() throws Exception {
}
//设置环境变量配置信息
@Override
public void setEnvironment(Environment environment) {
if (includeSpringEnv) {
// Get PropertySource mapped to 'dubbo.properties' in Spring Environment.
//获取配置资源映射到dubbo.properties
setExternalConfig(getConfigurations(getConfigFile(), environment));
//在Spring Environment里获取配置资源映射到application.dubbo.properties
setAppExternalConfig(getConfigurations(StringUtils.isNotEmpty(getAppConfigFile()) ? getAppConfigFile() : ("application." getConfigFile()), environment));
}
}
//获取配置
private Map<String, String> getConfigurations(String key, Environment environment) {
//通过传入的环境变量拿到配置
Object rawProperties = environment.getProperty(key, Object.class);
Map<String, String> externalProperties = new HashMap<>();
try {
//如果配置是map形式的,则将map放入到扩展配置中
if (rawProperties instanceof Map) {
externalProperties.putAll((Map<String, String>) rawProperties);
//如果是String的,则解析配置,同时将其放入到map中
} else if (rawProperties instanceof String) {
externalProperties.putAll(ConfigurationUtils.parseProperties((String) rawProperties));
}
//配置环境信息,同时扩展配置为空,则将配置资源放入到map中
//externalProperties.put(k, (String) v);
if (environment instanceof ConfigurableEnvironment && externalProperties.isEmpty()) {
ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment;
PropertySource propertySource = configurableEnvironment.getPropertySources().get(key);
if (propertySource != null) {
Object source = propertySource.getSource();
if (source instanceof Map) {
((Map<String, Object>) source).forEach((k, v) -> {
externalProperties.put(k, (String) v);
});
}
}
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
return externalProperties;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public Boolean getIncludeSpringEnv() {
return includeSpringEnv;
}
public void setIncludeSpringEnv(Boolean includeSpringEnv) {
this.includeSpringEnv = includeSpringEnv;
}
}
ConfigCenterConfig
ConfigCenterConfig里面的参数信息,由于里面基本上是参数信息,这里只放入参数信息:
代码语言:javascript复制//配置中心配置
public class ConfigCenterConfig extends AbstractConfig {
//原子类 inited
private AtomicBoolean inited = new AtomicBoolean(false);
//协议
private String protocol;
//地址
private String address;
//端口
private Integer port;
//集群 配置中心集群
private String cluster;
//命名空间 配置中心命名空间 dubbo
private String namespace = CommonConstants.DUBBO;
//配置中心 group dubbo
private String group = CommonConstants.DUBBO;
private String username;
private String password;
//超时时间
private Long timeout = 3000L;
//最高优先级 配置中心
private Boolean highestPriority = true;
private Boolean check = true;
//dubbo.properties 配置文件
private String configFile = CommonConstants.DEFAULT_DUBBO_PROPERTIES;
//appConfig文件
private String appConfigFile;
//参数信息
private Map<String, String> parameters;
//扩展配置
private Map<String, String> externalConfiguration;
//app扩展配置
private Map<String, String> appExternalConfiguration;
AbstractConfig
AbstractConfig:主要有几个方法,都是将参数添加到map中的,appendParameters、appendAttributes、appendAnnotation、addIntoConfigManager,refresh方法刷新配置信息等,这里以appendParamenters为例:
代码语言:javascript复制 //对参数进行append操作
//参数集合,用于URL.parameters
public static void appendParameters(Map<String, String> parameters, Object config) {
appendParameters(parameters, config, null);
}
//append参数信息
@SuppressWarnings("unchecked")
public 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();
//判断其是不是Getter方法,如果是get方法则为true
if (MethodUtils.isGetter(method)) {
//获取方法的注解参数
/**
* class ExampleConfig {
* // Dubbo will try to get "dubbo.example.alias_for_item=xxx" from .properties, if you want to use the original property
* // "dubbo.example.item=xxx", you need to set useKeyAsProperty=false.
* @Parameter(key = "alias_for_item")
* public getItem();
* }
* }
*/
Parameter parameter = method.getAnnotation(Parameter.class);
if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
continue;
}
//拿到参数的key
String key;
if (parameter != null && parameter.key().length() > 0) {
key = parameter.key();
} else {
key = calculatePropertyFromGetter(name);
}
//拿到最原始的参数信息,获取属性值
Object value = method.invoke(config);
String str = String.valueOf(value).trim();
if (value != null && str.length() > 0) {
//转义
if (parameter != null && parameter.escaped()) {
str = URL.encode(str);
}
if (parameter != null && parameter.append()) {
//做拼接操作
// default. 里获取,适用于 ServiceConfig =》ProviderConfig 、ReferenceConfig =》ConsumerConfig
String pre = parameters.get(key);
if (pre != null && pre.length() > 0) {
str = pre "," str;
}
}
//添加前缀信息
// 通过 `parameters` 属性配置,例如 `AbstractMethodConfig.parameters` 。
if (prefix != null && prefix.length() > 0) {
key = prefix "." key;
}
//放入key和value信息
parameters.put(key, str);
} else if (parameter != null && parameter.required()) {
throw new IllegalStateException(config.getClass().getSimpleName() "." key " == null");
}
//如果不是getter方法,看是不是参数getter方法
} else if (isParametersGetter(method)) {
//放到map中
Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);
parameters.putAll(convert(map, prefix));
}
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
appendParameter方法进行的操作:
1.首先对传入的config进行判断,如果为空,则直接返回
2.通过config通过反射拿到方法method()数组
3.对method()数组进行遍历,或者方法的名称
4.如果方法的名称是getter方法,则获取getter方法上的注解参数信息,从而拿到它的key,从而拿到value值
5.通过配置获取value值,而value值是通过调用method.invoke()反射的底层方法获取的。而获取之后,如果参数是匹配escaped()方法的话,则会调用URL.encode方法对value值进行encode。如果是参数是追加,则做拼接操作。
6.最终将key和value值放入到parameter中。
2.ServiceBean
先看类图信息:
从类图,我们可以看到:
serviceBean信息 继承 服务配置 并实现InitializingBean和DisposableBean,以及各种aware。因此可以看到其必定存在相关的BeanName、ApplicationContextEventPublisher、ApplicationContext、Disposable、InitialingzingBena相关的方法:可以看到,这里我们就不展开说明了,主要关注实现InitialingziingBean之后的AfterPropertiesSet方法。
代码语言:javascript复制public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware {
//复写afterPropertiesSet方法,设置path
@Override
public void afterPropertiesSet() throws Exception {
if (StringUtils.isEmpty(getPath())) {
if (StringUtils.isNotEmpty(beanName)
&& StringUtils.isNotEmpty(getInterface())
&& beanName.startsWith(getInterface())) {
//重点关注
setPath(beanName);
}
}
}
/**
* @since 2.6.5
*/
//进行exported
@Override
public void exported() {
super.exported();
// Publish ServiceBeanExportedEvent
publishExportEvent();
}
/**
* @since 2.6.5
*/
private void publishExportEvent() {
ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this);
applicationEventPublisher.publishEvent(exportEvent);
}
//可以看到都会指向SerivceBean和Serviceconfig
public ServiceBeanExportedEvent(ServiceBean serviceBean) {
super(serviceBean);
}
public ServiceConfigExportedEvent(ServiceConfig source) {
super(source);
}
}
ServiceBean
ServiceConfig
代码语言:javascript复制//服务配置信息 继承 服务配置基础信息 重要:export()方法
public class ServiceConfig<T> extends ServiceConfigBase<T> {
public void setPath(String path) {
this.path = path;
}
}
没有看到我们想要的信息,但是我们却可以在看到SerivceCofing中存在的信息:暴露和延迟:
ServiceConfig#export方法
代码语言:javascript复制public synchronized void export() {
if (!shouldExport()) {
return;
}
if (bootstrap == null) {
bootstrap = DubboBootstrap.getInstance();
bootstrap.init();
}
// 检测 provider 是否为空,为空则新建一个,并通过系统变量为其初始化
checkAndUpdateSubConfigs();
//init serviceMetadata
//初始化服务元数据
serviceMetadata.setVersion(version);
serviceMetadata.setGroup(group);
serviceMetadata.setDefaultGroup(group);
serviceMetadata.setServiceType(getInterfaceClass());
serviceMetadata.setServiceInterfaceName(getInterface());
serviceMetadata.setTarget(getRef());
//是否应该延迟
if (shouldDelay()) {
//延迟定时任务
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
//做暴露操作
doExport();
}
exported();
}
1.取消暴露服务,则直接返回
2.进行bootstrap处理,如果bootstrap为空,则首先进行实例化,然后进行初始化
3.检测 provider 是否为空,为空则新建一个,并通过系统变量为其初始化
4.初始化服务提供者的元数据信息
5.进行服务暴露前先判断是否进行延迟操作,如果进行延迟,执行定时任务操作
6.如果不延迟,则直接进行暴露操作
ServiceConfig#checkAndUpdateSubConfigs方法
进行检查和更新:
代码语言:javascript复制// 检测 provider 是否为空,为空则新建一个,并通过系统变量为其初始化
private void checkAndUpdateSubConfigs() {
// Use default configs defined explicitly with global scope
completeCompoundConfigs();
checkDefault();
checkProtocol();
// init some null configuration.
List<ConfigInitializer> configInitializers = ExtensionLoader.getExtensionLoader(ConfigInitializer.class)
.getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
configInitializers.forEach(e -> e.initServiceConfig(this));
// if protocol is not injvm checkRegistry
if (!isOnlyInJvm()) {
checkRegistry();
}
this.refresh();
if (StringUtils.isEmpty(interfaceName)) {
throw new IllegalStateException("<dubbo:service interface="" /> interface not allow null!");
}
//ref 非GenericService
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
} else {
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
//对interfaceClass,以及<dubbo:method>标签中的必要字段进行检查
checkInterfaceAndMethods(interfaceClass, getMethods());
//对ref进行合法性校验
checkRef();
//设置generic=false
generic = Boolean.FALSE.toString();
}
//local和stub在功能上应该是一样的,用于配置本地存根
//目前local已经废弃,stub已经替代stub
if (local != null) {
if ("true".equals(local)) {
local = interfaceName "Local";
}
Class<?> localClass;
try {
//获取本地存根类
localClass = ClassUtils.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 = ClassUtils.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);
}
}
checkStubAndLocal(interfaceClass);
ConfigValidationUtils.checkMock(interfaceClass, this);
ConfigValidationUtils.validateServiceConfig(this);
postProcessConfig();
}
进行延迟操作:
ServiceConfig#shouldDelay方法
代码语言:javascript复制//进行延迟操作
public boolean shouldDelay() {
Integer delay = getDelay();
return delay != null && delay > 0;
}
//获取延迟操作
@Override
public Integer getDelay() {
return (delay == null && provider != null) ? provider.getDelay() : delay;
}
进行服务暴露:
ServiceConfig#doExport方法
代码语言:javascript复制 //进行服务暴露
protected synchronized void doExport() {
//如果取消暴露,则抛异常,已经取消暴露
if (unexported) {
throw new IllegalStateException("The service " interfaceClass.getName() " has already unexported!");
}
//如果已经暴露,则直接返回
if (exported) {
return;
}
exported = true;
//如果路径为空,则给接口名称
if (StringUtils.isEmpty(path)) {
path = interfaceName;
}
//重要 做url暴露操作
doExportUrls();
}
1.首先判断是否取消服务暴露,如果是,则抛异常
2.如果已经暴露,直接返回
3.如果路径为空,则给定接口信息
4.做url暴露
ServiceConfig#doExportUrls方法
代码语言:javascript复制//暴露url操作
//格式:protocol://username:password@host:port/path?key=value&key=value
//比如:dubbo://127.0.0.1:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&default.delay=-1&default.retries=0&default.service.filter=demoFilter&delay=-1&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=19031&side=provider×tamp=1519651641799
@SuppressWarnings({"unchecked", "rawtypes"})
private void doExportUrls() {
//拿到ServiceRespository
ServiceRepository repository = ApplicationModel.getServiceRepository();
//拿到接口Class,注册服务信息
ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
//注册服务提供者的相关信息:服务的名称。引用信息实现类,服务描述信息、配置信息、服务的元数据信息
repository.registerProvider(
getUniqueServiceName(),
ref,
serviceDescriptor,
this,
serviceMetadata
);
//加载注册中心url:注册服务列表
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
//对协议配置配置遍历,并在每个协议下暴露服务 重要
for (ProtocolConfig protocolConfig : protocols) {
String pathKey = URL.buildKey(getContextPath(protocolConfig)
.map(p -> p "/" path)
.orElse(path), group, version);
// In case user specified path, register service one more time to map it to path.
repository.registerService(pathKey, interfaceClass);
// TODO, uncomment this line once service key is unified
serviceMetadata.setServiceKey(pathKey);
//组装url 非常重要
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
在组装url之前,会先进行注册服务提供者的相关信息,同时加载注册中心的url列表信息,对每个协议配置遍历,并在每个协议下暴露服务。由于注册服务信息是一个列表。
ServiceConfig#doExportUrlsFor1Protocol方法:
代码语言:javascript复制//组装url,url是dubbo配置的载体,通过url可让Dubbo的各种配置在各个模块之间传递。
//url之于dubbo,犹如水之于鱼,非常重要
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
/**========================组装url ===================**/
//拿到协议信息,如果协议名称为空,则设置协议名称默认为dubbo
String name = protocolConfig.getName();
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
//添加 side信息
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, PROVIDER_SIDE);
//通过反射将对象的字段信息添加到map中
ServiceConfig.appendRuntimeParameters(map);
AbstractConfig.appendParameters(map, getMetrics());
AbstractConfig.appendParameters(map, getApplication());
AbstractConfig.appendParameters(map, getModule());
// remove 'default.' prefix for configs from ProviderConfig
// appendParameters(map, provider, Constants.DEFAULT_KEY);
AbstractConfig.appendParameters(map, provider);
AbstractConfig.appendParameters(map, protocolConfig);
AbstractConfig.appendParameters(map, this);
MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
//外部化配置的时候用到
// 添加元数据信息
//dubbo.metadata-report.address=zookeeper://127.0.0.1:2181
if (metadataReportConfig != null && metadataReportConfig.isValid()) {
map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
}
//如果获取的方法为methodConfig的集合不为空,
// methodConfig中存储了<dubbo:method>标签的配置信息,则对其进行遍历
//服务端方法级别:
/**
* <dubbo:service interface="...">
* <dubbo:method name="..." loadbalance="roundrobin"/>
* </dubbo:service>
*/
/**
* <dubbo:reference>
* <dubbo:method name="findFoo" retries="2" />
* </dubbo:reference>
*/
if (CollectionUtils.isNotEmpty(getMethods())) {
for (MethodConfig method : getMethods()) {
AbstractConfig.appendParameters(map, method, method.getName());
String retryKey = method.getName() ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
if ("false".equals(retryValue)) {
map.put(method.getName() ".retries", "0");
}
}
//获取构造参数列表信息
List<ArgumentConfig> arguments = method.getArguments();
//检测type属性是否为空,或者空串
if (CollectionUtils.isNotEmpty(arguments)) {
for (ArgumentConfig argument : arguments) {
// convert argument type
if (argument.getType() != null && argument.getType().length() > 0) {
Method[] methods = interfaceClass.getMethods();
// visit all methods
if (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中的参数名称是否一致,不一致,则抛出异常
if (argument.getIndex() != -1) {
if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
AbstractConfig.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())) {
AbstractConfig.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属性,且index!=-1
//添加ArgumentConfig字段信息到map中
} else if (argument.getIndex() != -1) {
AbstractConfig.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
}
//检查generic是否为true,并且根据检测结果向map中添加不同的信息
if (ProtocolUtils.isGeneric(generic)) {
map.put(GENERIC_KEY, generic);
map.put(METHODS_KEY, ANY_VALUE);
//否则,获取版本号信息、如果版本信息不为空,同时长度>0,则放入版本信息
//同时为接口生成包装类,包装类中包含了接口的详细信息
} else {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put(REVISION_KEY, revision);
}
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
//添加方法到map中,如果包含多个方法名,则用逗号隔开,比如method=init,destory
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)), ","));
}
}
/**
* Here the token value configured by the provider is used to assign the value to ServiceConfig#token
*/
if(ConfigUtils.isEmpty(token) && provider != null) {
token = provider.getToken();
}
//添加token信息
if (!ConfigUtils.isEmpty(token)) {
if (ConfigUtils.isDefault(token)) {
//随机生成token信息
map.put(TOKEN_KEY, UUID.randomUUID().toString());
} else {
//添加token信息
map.put(TOKEN_KEY, token);
}
}
//init serviceMetadata attachments
//初始化服务元数据标签
serviceMetadata.getAttachments().putAll(map);
// export service
//host、port信息
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);
/**
* 组装url 重要
* URL url = new URL(name, host, port, (contextPath == null ||
* contextPath.length() == 0 ? "" : contextPath "/") path, map);
* map中包含的信息:版本、时间戳、方法名以及各种配置对象的字段信息
*/
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p "/" path).orElse(path), map);
// You can customize Configurator to append extra parameters
//你可以自定义配置追加额外参数信息
/**================ 组装完url,进行服务暴露,分为暴露到本地(jvm)和暴露到远程 ====================**/
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
//加载配置工厂,并生产配置实例,然后通过实例配置url
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
String scope = url.getParameter(SCOPE_KEY);
// don't export when none is configured
//如果scope==none,则什么都不做
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)) {
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));
//加载monitor链接
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
if (monitorUrl != null) {
//将监控url作为参数加入到url中
url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
if (url.getParameter(REGISTER_KEY, true)) {
logger.info("Register dubbo service " interfaceClass.getName() " url " url " to registry " registryURL);
} else {
logger.info("Export dubbo service " interfaceClass.getName() " to url " url);
}
}
// For providers, this is used to enable custom proxy to generate invoker
//为服务提供者ref生成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用于持有Invoker和ServiceConfig
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
//暴露服务,并生成Exporter
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
//不存在注册找那个性,仅暴露服务
} else {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " interfaceClass.getName() " to url " url);
}
//重要 invoker
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
*/
WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE));
if (metadataService != null) {
metadataService.publishServiceDefinition(url);
}
}
}
this.urls.add(url);
}
首先它会进行url的组装,然后再进行服务暴露:而服务的暴露又分为本地暴露(jvm)和远程暴露。url在dubbo中是非常重要的。
一、组装过程:
1.首先添加协议信息,如果协议为空,则默认为dubbo协议
2.添加side信息信息
3.将原来的一些相关的服务提供者信息的map添加到map。通过反射拿到相关字段信息添加到map中。
4.外部化配置的时候,添加元数据信息
5.如果获取的方法为methodConfig的集合不为空,methodConifg中存储了dubbo:method标签的配置信息,对其进行遍历
6.获取 ArgumentConfig 列表,遍历ArgumentConifg如果type不为空,则通过反射获取interfaceClass的方法列表,通过比较方法名获取目标方法;通过反射获取目标方法的参数类型数组
7.检查genetic是否为true,并根据不同的情况添加不同的信息,如果不存在,则添加默认的
8.添加token信息,默认采用uuid生成,如果给定,则采用给定的
9.初始化服务元数据标签信息
appendParameters 这个方法出现的次数比较多,该方法用于将对象字段信息添加到 map 中。实现上则是通过反射获取目标对象的getter 方法,并调用该方法获取属性值。然后再通过 getter 方法名解析出属性名,比如从方法名 getName 中可解析出属性 name。如果用户传入了属性名前缀,此时需要将属性名加入前缀内容。最后将 <属性名,属性值> 键值对存入到 map 中就行了。
二、暴露服务
分为两种:本地服务暴露(jvm)和远程服务暴露,通过scope可以看到分为三种情况:
scope=none 不暴露服务
scope!=remote 暴露服务到本地,exportLocal(url)
scope!=local 暴露服务到远程,暴露到远程,会加载监控monitor链接,将其添加到url中,为服务提供类ref生产invoker,DelagateProviderMetadataInvoker用于持有Invoker和ServiceConfig,暴露服务,并生成Exporter。
因此我们必然需要对Invoker进行研究,也即
代码语言:javascript复制/**
* ProxyFactory. (API/SPI, Singleton, ThreadSafe)
*/
@SPI("javassist")
public interface ProxyFactory {
@Adaptive({PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
后面对invoker进行学习!