组装URL
本节,全部介绍:doExportUrlsForlProtocol()方法,位于ServiceConfig类中,我们分为四个段落,进行阅读
我们先看一下这个方法的传入参数:ProtocolConfig protocolConfig, List<URL> registryURLs
第一个参数是协议,它也继承自AbstractConfig 第二个是URL(映射到一个注册中心)
看一下部分字段 ProcotolConfig类 ,字段真的超级多
public class ProtocolConfig extends AbstractConfig {
private String name; //protocol name
private String host; //service ip address
private Integer port;
private String contextpath;
private String threadpool;
private String threadname;
private Integer corethreads; //threadpool size
private Integer threads; //max size
private Integer iothreads; //io size
...
}
第一段
String name = protocolConfig.getName();
//如果协议为空,则将协议名设置为 dubbo
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, PROVIDER_SIDE); //添加side
//通过反射,将对象的信息放到map中,用于后续构造URL,
// 1号
ServiceConfig.appendRuntimeParameters(map);
// 2号
AbstractConfig.appendParameters(map, getApplication()); //applicationConfig
AbstractConfig.appendParameters(map, getModule()); //ModuleConfig
AbstractConfig.appendParameters(map, provider); //ProviderConfig
AbstractConfig.appendParameters(map, protocolConfig); //protocolConfig
AbstractConfig.appendParameters(map, this);
MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
if (metadataReportConfig != null && metadataReportConfig.isValid()) {
map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
}
第二段
方法参数设置,检测 <dubbo:method> 标签中的配置信息,并将相关配置添加到 map 中,MethodConfig 中存储了 <dubbo:method> 标签的配置信息,这个标签是<dubbo:service>下面的子标签
一般我们写的,没有使用method,而是直接暴露实现类,所以该段可以跳过。 第二段,比较纯粹,没有涉及到其他未知的方法
if (CollectionUtils.isNotEmpty(getMethods())) {
for (MethodConfig method : getMethods()) { // 【第一层】 for循环
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(); //获取方法参数(自定义的 方法参数类,内设置了下标等属性)
if (CollectionUtils.isNotEmpty(arguments)) {
for (ArgumentConfig argument : arguments) { //【第二层】 遍历参数
// 检测 type是否为空
if (argument.getType() != null && argument.getType().length() > 0) {
Method[] methods = interfaceClass.getMethods(); //获得接口方法
// 保证,暴露的方法 能对应上接口的方法,不然该方法是无效的( 安全防护)
if (methods.length > 0) {
for (int i = 0; i < methods.length; i++) { //【第三层】 遍历接口方法
String methodName = methods[i].getName();
if (methodName.equals(method.getName())) { //找到实现类和接口方法对应的 (双重遍历)
Class<?>[] argtypes = methods[i].getParameterTypes(); //获取接口方法的参数类型
//
if (argument.getIndex() != -1) { //参数 顺序要对齐
if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex()); //将信息,放入 map
} else {
throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
}
} else { //如果没有对齐,遍历一下,是否存在参数
for (int j = 0; j < argtypes.length; j++) {
Class<?> argclazz = argtypes[j];
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());
}
}
}
}
}
}
}
} 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
}
第三段
主要是检测一些配置,是否正确,并且进行URL的组装,重点是对接口中的方法进行处理,
if (ProtocolUtils.isGeneric(generic)) {
map.put(GENERIC_KEY, generic);
map.put(METHODS_KEY, ANY_VALUE);
} else {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put(REVISION_KEY, revision);
}
// 为接口生成包裹类 Wrapper,Wrapper 中包含了接口的详细信息,比如接口方法名数组,字段信息等
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); // 【入】
// 添加方法名到 map 中,如果包含多个方法名,则用逗号隔开,比如 method = init,destroy
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)), ","));
}
}
if(ConfigUtils.isEmpty(token) && provider != null) {
token = provider.getToken();
}
if (!ConfigUtils.isEmpty(token)) {
if (ConfigUtils.isDefault(token)) {
map.put(TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(TOKEN_KEY, token);
}
}
//init serviceMetadata attachments
serviceMetadata.getAttachments().putAll(map);
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);
//【url组装】
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
我们 看一下,如何包装interfaceClass到wrapper中的 Wrapper类
public static Wrapper getWrapper(Class<?> c) {
//从缓冲中获取,
while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.
{
c = c.getSuperclass();
}
if (c == Object.class) {
return OBJECT_WRAPPER;
}
//未命中,创建一个,并且放入缓冲
return WRAPPER_MAP.computeIfAbsent(c, key -> makeWrapper(key)); //【入】
}
如果缓冲不存在,则需要makeWrapper,这里就有很多代码了!它应该是一种代理操作,我们需要分成5段阅读
3.1 makeWrapper()第一段 :容器准备
可见,创建了三个方法的代码,存储在实体中。三个方法分别是针对类的字段,类的方法,字段具有set/get两个方法,由于我们的demo接口中,只有方法,所以重点在c3代码中。
if (c.isPrimitive()) { //检测c是否为基本类型,若是 则抛出异常
throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);
}
String name = c.getName();
ClassLoader cl = ClassUtils.getClassLoader(c);
//c1用于存储setPropertyValue方法代码,下面两个以此内推
StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");
//生成类型转换代码及异常捕捉代码
c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
// pts 用于存储成员变量名和类型
Map<String, Class<?>> pts = new HashMap<>(); // <property name, property types>
// ms 用于存储方法描述信息(方法签名),以及method实例
Map<String, Method> ms = new LinkedHashMap<>(); // <method desc, Method instance>
//mns 方法名字
List<String> mns = new ArrayList<>(); // method names.
//dmns 用于存储“定义在当前类中的方法” 的名称
List<String> dmns = new ArrayList<>(); // declaring method names.
看一个setPropertyValue()方法的代码:它还没有完成,缺少 }
public void setPropertyValue(Object o, String n, Object v){
org.apache.dubbo.demo.DemoService w;
try{
w = ((org.apache.dubbo.demo.DemoService)$1);
}catch(Throwable e){
throw new IllegalArgumentException(e);
}
3.2 makeWrapper() 第二段
获取全部public 字段,由于我们的demo中没有成员变量,所以跳过了,但是这里要记住,处理了接口中的字段!
for (Field f : c.getFields()) {
String fn = f.getName();
Class<?> ft = f.getType();
if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers())) {
continue;
}
//生成条件判断及赋值语句
// if( $2.equals("name") ) { w.name = (java.lang.String) $3; return;}
c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");
c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");
// 存储 <字段名, 字段类型> 键值对到 pts 中
pts.put(fn, ft);
}
3.3 makeWrapper() 第三段:方法处理
Method[] methods = c.getMethods();
boolean hasMethod = hasMethods(methods); //是否有自己写的方法(除Object方法)
if (hasMethod) {
c3.append(" try{");
for (Method m : methods) {
//ignore(忽略) Object's method.
if (m.getDeclaringClass() == Object.class) {
continue;
}
String mn = m.getName();
//生成方法判断语句,比如: if("syaHello".equals($2)
c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");
int len = m.getParameterTypes().length;
c3.append(" && ").append(" $3.length == ").append(len);
boolean override = false;
for (Method m2 : methods) { //二层 【for】
if (m != m2 && m.getName().equals(m2.getName())) { //有重载,方法对象不同,名字一样
override = true;
break;
}
}
if (override) {
if (len > 0) {
for (int l = 0; l < len; l++) {
//生成参数类型 进行检测代码
c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
.append(m.getParameterTypes()[l].getName()).append("\")");
}
}
}
c3.append(" ) { ");
// 根据返回值类型生成目标方法调用语句
if (m.getReturnType() == Void.TYPE) {
c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
} else {
c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");
}
c3.append(" }");
// 添加方法名到 mns 集合中
mns.add(mn);
if (m.getDeclaringClass() == c) {
dmns.add(mn);
}
ms.put(ReflectUtils.getDesc(m), m);
}
c3.append(" } catch(Throwable e) { ");
c3.append(" throw new java.lang.reflect.InvocationTargetException(e); ");
c3.append(" }");
}
c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");
处理完毕method之后,C3结构:
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{
org.apache.dubbo.demo.DemoService w;
try{
w = ((org.apache.dubbo.demo.DemoService)$1);
}catch(Throwable e){
throw new IllegalArgumentException(e); }
try{
if( "sayHello".equals( $2 ) && $3.length == 1 ) {
return ($w)w.sayHello((java.lang.String)$4[0]);
}
if( "sayHelloAsync".equals( $2 ) && $3.length == 1 ) {
return ($w)w.sayHelloAsync((java.lang.String)$4[0]);
}
} catch(Throwable e) {
throw new java.lang.reflect.InvocationTargetException(e);
}
throw new org.apache.dubbo.common.bytecode.NoSuchMethodException("Not found method \""+$2+"\" in class org.apache.dubbo.demo.DemoService.");
}
3.4 makeWrapper第四段:处理get/set方法
Matcher matcher;
for (Map.Entry<String, Method> entry : ms.entrySet()) {
String md = entry.getKey();
Method method = entry.getValue();
if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
String pn = propertyName(matcher.group(1));
c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
pts.put(pn, method.getReturnType());
} else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) {
String pn = propertyName(matcher.group(1));
c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
pts.put(pn, method.getReturnType());
} else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
Class<?> pt = method.getParameterTypes()[0];
String pn = propertyName(matcher.group(1));
c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt, "$3")).append("); return; }");
pts.put(pn, pt);
}
}
c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");
c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");
3.5 makeWrapper第五段:类生成
long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
//创建 类生成器
ClassGenerator cc = ClassGenerator.newInstance(cl);
//设置 类名和超类
cc.setClassName((Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw") + id);
cc.setSuperClass(Wrapper.class);
//添加构造方法,字段
cc.addDefaultConstructor();
cc.addField("public static String[] pns;"); // property name array.
cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.
cc.addField("public static String[] mns;"); // all method name array.
cc.addField("public static String[] dmns;"); // declared method name array.
for (int i = 0, len = ms.size(); i < len; i++) {
cc.addField("public static Class[] mts" + i + ";");
}
//添加方法代码
cc.addMethod("public String[] getPropertyNames(){ return pns; }");
cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
cc.addMethod("public String[] getMethodNames(){ return mns; }");
cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
cc.addMethod(c1.toString());
cc.addMethod(c2.toString());
cc.addMethod(c3.toString());
try {
//生成类
Class<?> wc = cc.toClass();
//设置字段值
wc.getField("pts").set(null, pts);
wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
wc.getField("mns").set(null, mns.toArray(new String[0]));
wc.getField("dmns").set(null, dmns.toArray(new String[0]));
int ix = 0;
for (Method m : ms.values()) {
wc.getField("mts" + ix++).set(null, m.getParameterTypes());
}
//创建wrapper实例
return (Wrapper) wc.newInstance();
}
最后,利用已存在的URL,+map生成新的URL,它是dubbo重写的,不是jdk的url
class URL implements Serializable {
private static final long serialVersionUID = -1985165475234910535L;
private final String protocol;
private final String username;
private final String password;
// by default, host to registry
private final String host;
// by default, port to registry
private final int port;
private final String path;
private final Map<String, String> parameters;
private final Map<String, Map<String, String>> methodParameters;
// ==== cache ====
private volatile transient Map<String, Number> numbers;
private volatile transient Map<String, Map<String, Number>> methodNumbers;
private volatile transient Map<String, URL> urls;
private volatile transient String ip;
private volatile transient String full;
private volatile transient String identity;
private volatile transient String parameter;
private volatile transient String string;
private transient String serviceKey;
private transient String address;
...
get/set
}
第四段
服务暴露
String scope = url.getParameter(SCOPE_KEY);
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url); // ---------【本地服务暴露】-----------
}
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));
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
}
String proxy = url.getParameter(PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(PROXY_KEY, proxy);
}
//-----【 通过动态代理 转换成 invoker 】--------------
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
//服务暴露后,向注册中心,注册信息 【RegistryProtocol?】 官方走的是这里,但是不知道是不是这个入口进去的!
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);
}
WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE));
if (metadataService != null) {
metadataService.publishServiceDefinition(url);
}
}
}
this.urls.add(url);
具体到每个协议的暴露,请看我们下一节:export()