源码结构图:
服务消费者的API配置:
mport org.apache.dubbo.rpc.config.ApplicationConfig;
import org.apache.dubbo.rpc.config.RegistryConfig;
import org.apache.dubbo.rpc.config.ConsumerConfig;
import org.apache.dubbo.rpc.config.ReferenceConfig;
import com.xxx.XxxService;
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("yyy");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("10.20.130.230:9090");
registry.setUsername("aaa");
registry.setPassword("bbb");
// 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接
// 引用远程服务
ReferenceConfig<XxxService> reference = new ReferenceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
reference.setApplication(application);
reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
reference.setInterface(XxxService.class);
reference.setVersion("1.0.0");
// 和本地bean一样使用xxxService
XxxService xxxService = reference.get(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用
private void init() {
if (initialized) {
return;
}
initialized = true;
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");
}
// get consumer's global configuration
checkDefault();
appendProperties(this);
if (getGeneric() == null && getConsumer() != null) {
setGeneric(getConsumer().getGeneric());
}
if (ProtocolUtils.isGeneric(getGeneric())) {
interfaceClass = GenericService.class;
} else {
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
checkInterfaceAndMethods(interfaceClass, methods);
}
String resolve = System.getProperty(interfaceName);
String resolveFile = null;
if (resolve == null || resolve.length() == 0) {
resolveFile = System.getProperty("dubbo.resolve.file");
if (resolveFile == null || resolveFile.length() == 0) {
File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
if (userResolveFile.exists()) {
resolveFile = userResolveFile.getAbsolutePath();
}
}
if (resolveFile != null && resolveFile.length() > 0) {
Properties properties = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(new File(resolveFile));
properties.load(fis);
} catch (IOException e) {
throw new IllegalStateException("Unload " + resolveFile + ", cause: " + e.getMessage(), e);
} finally {
try {
if (null != fis) fis.close();
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
}
resolve = properties.getProperty(interfaceName);
}
}
if (resolve != null && resolve.length() > 0) {
url = resolve;
if (logger.isWarnEnabled()) {
if (resolveFile != null) {
logger.warn("Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");
} else {
logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");
}
}
}
if (consumer != null) {
if (application == null) {
application = consumer.getApplication();
}
if (module == null) {
module = consumer.getModule();
}
if (registries == null) {
registries = consumer.getRegistries();
}
if (monitor == null) {
monitor = consumer.getMonitor();
}
}
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();
}
}
checkApplication();
checkStub(interfaceClass);
checkMock(interfaceClass);
Map<String, String> map = new HashMap<String, String>();
Map<Object, Object> attributes = new HashMap<Object, Object>();
map.put(Constants.SIDE_KEY, Constants.CONSUMER_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()));
}
if (!isGeneric()) {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put("revision", revision);
}
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("NO method found in service interface " + interfaceClass.getName());
map.put("methods", Constants.ANY_VALUE);
} else {
map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
map.put(Constants.INTERFACE_KEY, interfaceName);
appendParameters(map, application);
appendParameters(map, module);
appendParameters(map, consumer, Constants.DEFAULT_KEY);
appendParameters(map, this);
String prefix = StringUtils.getServiceKey(map);
if (methods != null && !methods.isEmpty()) {
for (MethodConfig method : methods) {
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");
}
}
appendAttributes(attributes, method, prefix + "." + method.getName());
checkAndConvertImplicitConfig(method, map, attributes);
}
}
String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
if (hostToRegistry == null || hostToRegistry.length() == 0) {
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
}
map.put(Constants.REGISTER_IP_KEY, hostToRegistry);
//attributes are stored by system context.
StaticContext.getSystemContext().putAll(attributes);
ref = createProxy(map);
ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
}
超级长的代码,不着急。一行一行的啃。
//如果已经初始化,那么就直接返回。
if (initialized) {
return;
}
initialized = true;
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");
}
//拼接属性的配置
checkDefault();
appendProperties(this);
关于泛化接口文档 (http://dubbo.apache.org/zh-cn/blog/dubbo-generic-invoke.html)
//泛化接口的实现。
if (getGeneric() == null && getConsumer() != null) {
setGeneric(getConsumer().getGeneric());
}
if (ProtocolUtils.isGeneric(getGeneric())) {
interfaceClass = GenericService.class;
} else {
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
//检查接口和方法 这个超简单可以看源码。
checkInterfaceAndMethods(interfaceClass, methods);
}
直连提供者文档http://dubbo.apache.org/zh-cn/docs/user/demos/explicit-target.html
//直连 第一优先级 通过-D 的参数指定。
String resolve = System.getProperty(interfaceName);
String resolveFile = null;
if (resolve == null || resolve.length() == 0) {
// 第二优先级 。先获取 dubbo.resolve.file文件,无需配置。
resolveFile = System.getProperty("dubbo.resolve.file");
if (resolveFile == null || resolveFile.length() == 0) {
File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
if (userResolveFile.exists()) {
resolveFile = userResolveFile.getAbsolutePath();
}
}
if (resolveFile != null && resolveFile.length() > 0) {
Properties properties = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(new File(resolveFile));
properties.load(fis);
} catch (IOException e) {
throw new IllegalStateException("Unload " + resolveFile + ", cause: " + e.getMessage(), e);
} finally {
try {
if (null != fis) fis.close();
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
}
resolve = properties.getProperty(interfaceName);
}
}
if (resolve != null && resolve.length() > 0) {
//如果存在则设置直连的url
url = resolve;
if (logger.isWarnEnabled()) {
if (resolveFile != null) {
logger.warn("Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");
} else {
logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");
}
}
}
接下来的代码将ConsumerConfig、ModuleConfig、ApplicationConfig 配置对象,复制 application module registries monitor 给 ReferenceConfig ( 自己 )。
if (consumer != null) {
if (application == null) {
application = consumer.getApplication();
}
if (module == null) {
module = consumer.getModule();
}
if (registries == null) {
registries = consumer.getRegistries();
}
if (monitor == null) {
monitor = consumer.getMonitor();
}
}
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();
}
}
之后将各种属性加入到map中。然后checkAndConvertImplicitconfig()方法。检查属性和集合中的事件通知方法是否正确。若正确,进行转换。
appendProperties(AbstractConfig config) 方法。该方法读取启动参数变量和 properties 配置到配置对象。
protected static void appendProperties(AbstractConfig config) {
if (config == null) {
return;
}
String prefix = "dubbo." + getTagName(config.getClass()) + ".";
Method[] methods = config.getClass().getMethods();
for (Method method : methods) {
try {
String name = method.getName();
//方法是public的set方法。唯一参数必须是基本数据类型。
if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(method.getModifiers())
&& method.getParameterTypes().length == 1 && isPrimitive(method.getParameterTypes()[0])) {
String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), ".");
String value = null;
//启动参数变量。从带有Config#id的配置中获取。eg:`dubbo.application.demo-provider.name` 。
if (config.getId() != null && config.getId().length() > 0) {
String pn = prefix + config.getId() + "." + property;
value = System.getProperty(pn);
if (!StringUtils.isBlank(value)) {
logger.info("Use System Property " + pn + " to config dubbo");
}
}
//从不带“config#id”的配置中获取 `dubbo.application.name` 。
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) {
// 覆盖优先级为:启动参数变量 > XML 配置 > properties 配置,因此需要使用 getter 判断 XML 是否已经设置
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) {
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));
}
//看下面输出
if(value != null){
System.out.println("config 的 id :" + config.getId());
System.out.println("方法 的 属性 :" +method.getName());
System.out.println("对应方法的值:" + value);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
输出
config 的 id :demo-provider
方法 的 属性 :setQosPort
对应方法的值:22222
这个里面就设置了默认的QosPort的值。