Dubbo消费者代理的创建
来源:互联网 发布:网络布线收费标准 编辑:程序博客网 时间:2024/06/08 06:59
在消费者端,dubbo通过AnnotationBean类实现了BeanPostProcessor接口用来对beanFactory的中bean进行相应的处理。
关于消费者的bean以及bean中@Reference注解的处理在AnnotationBean的postProcessBeforeInitialization()方法当中。
对于bean中采用了@Reference注解的属性的处理在下面这段代码中。
Field[] fields = bean.getClass().getDeclaredFields();for (Field field : fields) { try { if (! field.isAccessible()) { field.setAccessible(true); } Reference reference = field.getAnnotation(Reference.class); if (reference != null) { Object value = refer(reference, field.getType()); if (value != null) { field.set(bean, value); } } } catch (Throwable e) { logger.error("Failed to init remote service reference at filed " + field.getName() + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e); }}
在这里将会遍历bean当中所有的属性,当找到带有@Reference注解的属性的时候,马上会尝试通过refer()方法完成对于@Reference的属性类型类的代理生成,以便在下面的代码中通过set()方法将代理了相应所需要代理的方法的代理set()进bean相应的的属性上。
看到实现同样实现在AnnotationBean类中的refer()方法。
String interfaceName;if (! "".equals(reference.interfaceName())) { interfaceName = reference.interfaceName();} else if (! void.class.equals(reference.interfaceClass())) { interfaceName = reference.interfaceClass().getName();} else if (referenceClass.isInterface()) { interfaceName = referenceClass.getName();} else { throw new IllegalStateException("The @Reference undefined interfaceClass or interfaceName, and the property type " + referenceClass.getName() + " is not a interface.");}String key = reference.group() + "/" + interfaceName + ":" + reference.version();ReferenceBean<?> referenceConfig = referenceConfigs.get(key);
在refer()方法的一开始,会根据@Reference的配置,或者本身配置了@Reference注解属性的实现接口来确定这个属性的接口名称(优先选择配置在注解当中的接口属性),因此将会构造成这个ReferenceBean的key(由reference的组名,接口名称以及版本号组成),如果在这里之前的bean已经构造过相同key的referenceBean,那么可以直接将直接的referenceBean取出而不用重新创建新的referenceBean来创建代理。
当然,如果没有通过key取得到相应的referenceBean的话,将会在下面的代码当中重新创建referenceBean,并根据Spring所提供的的上下文将referenceBean所需要的属性配置在ReferenceBean当中。
if (reference.consumer() != null && reference.consumer().length() > 0) { referenceConfig.setConsumer((ConsumerConfig)applicationContext.getBean(reference.consumer(), ConsumerConfig.class));}if (reference.monitor() != null && reference.monitor().length() > 0) { referenceConfig.setMonitor((MonitorConfig)applicationContext.getBean(reference.monitor(), MonitorConfig.class));}if (reference.application() != null && reference.application().length() > 0) { referenceConfig.setApplication((ApplicationConfig)applicationContext.getBean(reference.application(), ApplicationConfig.class));}if (reference.module() != null && reference.module().length() > 0) { referenceConfig.setModule((ModuleConfig)applicationContext.getBean(reference.module(), ModuleConfig.class));}if (reference.consumer() != null && reference.consumer().length() > 0) { referenceConfig.setConsumer((ConsumerConfig)applicationContext.getBean(reference.consumer(), ConsumerConfig.class));}try { referenceConfig.afterPropertiesSet();} catch (RuntimeException e) { throw (RuntimeException) e;} catch (Exception e) { throw new IllegalStateException(e.getMessage(), e);}
以上面的代码为例子,如果这@Reference注解当中配置了相应的监控中心消费者等属性,都将会在这里尝试从spring的上下文当中去取得相应的bean注入到referenceBean当中。
在这之后调用referenceBean的afterPropertiesSet()方法。
在afterPropertiesSet()方法中,首先是对在@Reference注解当中并没有进行配置的属性进行设置。以Registry(注册中心)为例子。
if ((getRegistries() == null || getRegistries().size() == 0) && (getConsumer() == null || getConsumer().getRegistries() == null || getConsumer().getRegistries().size() == 0) && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) { 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.size() > 0) { super.setRegistries(registryConfigs); } }}
如果在之前的注解当中并没有配置注册中心或者在referenceBean的消费者配置以及应用配置中都没有配置上注册中心的属性,那么将会遍历Spring上下文当中所有RegistryConfig类配置在ReferenceBean的超类ReferenceConfig当中。
在这个方法中,针对其他属性,例如moniter监控中心的操作与这里的注册中心的操作完全类似。
之后在确认所有属性配置完毕,并且消费者也已经初始化完毕之后,将会通过getObject()方法尝试取得referenceBean当中的ref属性,而这个属性恰恰就是代理了referenceBean当中消费者所要调用方法的代理类。
public Object getObject() throws Exception { return get();}public synchronized T get() { if (destroyed){ throw new IllegalStateException("Already destroyed!"); } if (ref == null) { init(); } return ref;}
由上文可见,当第一次调用getObject()方法的时候将会毫无疑问的调用ReferenceConfig的init()方法。
在init()方法的开头,仍旧是检察ReferenceBean的属性的配置是否已经完毕,并在这里对之前方法仍旧没有配置的属性进行配置。同时针对ReferenceBean的接口以及所要实现代理的方法进行验证,该方法是否是该接口下面的方法。if (methods != null && methods.size() > 0) { for (MethodConfig methodBean : methods) { String methodName = methodBean.getName(); if (methodName == null || methodName.length() == 0) { throw new IllegalStateException("<dubbo:method> name attribute is required! Please check: <dubbo:service interface=\"" + interfaceClass.getName() + "\" ... ><dubbo:method name=\"\" ... /></<dubbo:reference>"); } boolean hasMethod = false; for (java.lang.reflect.Method method : interfaceClass.getMethods()) { if (method.getName().equals(methodName)) { hasMethod = true; break; } } if (!hasMethod) { throw new IllegalStateException("The interface " + interfaceClass.getName() + " not found method " + methodName); } }}
在上面的代码中确保了所要代理的方法一定是接口所声明的方法。
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.getVersion());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);
在接下来,将会构造两个hashMap,其中的map将会存放是实现代理的必要属性,也将是之后生成代理的重要参数。
在这里,消费者的身份属性,版本属性,创建时间,进程id都将在这里被设置在map当中。如果没有才用泛化引用,在这里会给非动态类的将要被代理的接口生产相应的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; Wrapper ret = WRAPPER_MAP.get(c); if( ret == null ) { ret = makeWrapper(c); WRAPPER_MAP.put(c,ret); } return ret; }
可以看到,在这里会给接口的父类创建wrapper,如果父类直接是Object,那么直接会返回默认的ObjectWrapper,但是如果父类不是,那么将会通过makeWrapper()方法动态生成wrapper。
Wrapper通过包装目标类,动态根据目标类生成相应的get和set方法。拿field属性来做例子。
StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");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); }");for( Field f : c.getFields() ){ String fn = f.getName(); Class<?> ft = f.getType(); if( Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers()) ) continue; 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.put(fn, ft);}
在这里动态根据目标类的属性生成了相应的针对被包装类的get和set方法用来在接下里的操作可以方便取得目标的属性。同理被包装类的方法,经过wrapper的包装,方法的取得也动态生成了相应的方法。
在通过wrapper取得相应的方法之后,将所有的方法通过逗号隔开组成新的字符串放在map当中。
同时,在AbstractConfig类中,给出了appendParameters()方法。
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(); if ((name.startsWith("get") || name.startsWith("is")) && ! "getClass".equals(name) && Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == 0 && isPrimitive(method.getReturnType())) { Parameter parameter = method.getAnnotation(Parameter.class); if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) { continue; } int i = name.startsWith("get") ? 3 : 2; String prop = StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), "."); String key; if (parameter != null && parameter.key() != null && parameter.key().length() > 0) { key = parameter.key(); } else { key = prop; } Object value = method.invoke(config, new Object[0]); 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()) { String pre = (String)parameters.get(Constants.DEFAULT_KEY + "." + key); if (pre != null && pre.length() > 0) { str = pre + "," + str; } pre = (String)parameters.get(key); if (pre != null && pre.length() > 0) { str = pre + "," + str; } } 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"); } } else if ("getParameters".equals(name) && Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == 0 && method.getReturnType() == Map.class) { Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]); if (map != null && map.size() > 0) { 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); } }}
在这个方法中,将会遍历目标类当中的is和get方法,并通过反射获得相应的值,如果在该方法的上面实现了@Parameter注解,那么还将会对相应的值进行url编码,在获得相应的值之后通过注解当中配置的key或者属性名加上默认的前缀,作为键值对存放在map中,同样作为创建代理的参数存放在map中。在具体的参数获得中,application,Consumer,module以及该referenceBean和目标接口下的get和is方法都会把属性存放在map中,在init()方法的最后通过createProxy()方法,将Map作为参数,获取代理。
在createProxy()方法当中,首先,会根据是否配置了url来确定是否配置在了一开始的ReferenceBean当中,如果没有,则判断本地是否有接口暴露,如果有则采用本地服务。
这里默认不是本地服务,而是配置了注册中心往下走。
如果没有配置url在referenceBean当中,则会loadRegistries()当中url的数组。
protected List<URL> loadRegistries(boolean provider) { checkRegistry(); List<URL> registryList = new ArrayList<URL>(); if (registries != null && registries.size() > 0) { for (RegistryConfig config : registries) { String address = config.getAddress(); 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; } if (address != null && address.length() > 0 && ! RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) { Map<String, String> map = new HashMap<String, String>(); appendParameters(map, application); appendParameters(map, config); map.put("path", RegistryService.class.getName()); map.put("dubbo", Version.getVersion()); map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis())); if (ConfigUtils.getPid() > 0) { map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid())); } if (! map.containsKey("protocol")) { if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) { map.put("protocol", "remote"); } else { map.put("protocol", "dubbo"); } } List<URL> urls = UrlUtils.parseURLs(address, map); for (URL url : urls) { url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol()); url = url.setProtocol(Constants.REGISTRY_PROTOCOL); if ((provider && url.getParameter(Constants.REGISTER_KEY, true)) || (! provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) { registryList.add(url); } } } } } return registryList;}
在loadRegistries方法中,首先会遍历所有的registryConfig,优先配置url为配置文件的url,如果没有则为本身所配置的url。接下里跟之前准备创建代理的参数的map一样,在这里类似的跟之前的操作一样构造url以及map。具体的url构造在urlUtils中实现。这里不展开。
在根据注册中心构造完毕url之后,也会根据是否配置了监控中心的url,添加监控中心的url属性,最后,所有参数将会被构造成get的形式作为最后的url保存下来。
在下面,每一个生成的url都会生成对应的invoker。
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();URL registryURL = null;for (URL url : urls) { invokers.add(refprotocol.refer(interfaceClass, url)); if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { registryURL = url; // 用了最后一个registry url }}
假如这里采用了dubbo协议,这里将会生成dubbo invoker。
这里生成的invoker将在方法最后最后在ProxyFactory当中通过getProxy()获得最后所需要的代理对象并返回。
在AbstractProxyFactory当中的getProxy()方法中。
public <T> T getProxy(Invoker<T> invoker) throws RpcException { Class<?>[] interfaces = null; String config = invoker.getUrl().getParameter("interfaces"); if (config != null && config.length() > 0) { String[] types = Constants.COMMA_SPLIT_PATTERN.split(config); if (types != null && types.length > 0) { interfaces = new Class<?>[types.length + 2]; interfaces[0] = invoker.getInterface(); interfaces[1] = EchoService.class; for (int i = 0; i < types.length; i ++) { interfaces[i + 1] = ReflectUtils.forName(types[i]); } } } if (interfaces == null) { interfaces = new Class<?>[] {invoker.getInterface(), EchoService.class}; } return getProxy(invoker, interfaces);}
在这里,在除了定义在了invoker中的接口外,还将会固定将所代理的接口以及EchoService接口加在代理的类型当中。
接下来以jdk创建代理为例子,直接就在JdkProxyFactory中的getProxy()方法通过jdk实现了Invoker以及接口实现了关于Invoker的代理创建。
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));}
参数与一般的jdk代理实现并无区别,只是Invoker在最后还是作为参数用来还是生成被代理鄂最终类InvokerInvocationHandler。
消费者的代理创建就此结束。
- Dubbo消费者代理的创建
- 用maven创建dubbo消费者
- dubbo 创建服务代理
- Dubbo的Javassist代理
- 阿里dubbo框架使用系列:服务提供者和消费者的创建和使用
- dubbo 搭建 创建消费者 调用消费者 即是消费者又是提供者(六)
- Dubbo/Dubbox的服务消费(一)- 服务代理的创建
- Dubbo消费者配置
- Dubbo 消费者启动流程
- Dubbo 接口 、提供者、消费者
- dubbo管控台不显示消费者
- dubbo 服务消费者初始化
- dubbo中消费者配置文件
- Dubbo之服务消费者Web应用war包的部署
- 基于Dubbo协议的消费者示例及详解(三)
- 构建dubbo服务消费者web应用的war包
- Dubbo消费者无法连接到生产者提供的服务
- maven项目搭建dubbo的消费者和生产者
- Git-多分支管理-attempt
- 一位程序员工作10年总结的13个忠告
- 14. Longest Common Prefix
- unity3d 第五天 天空盒
- 简单的数据库增删改查语句
- Dubbo消费者代理的创建
- Codeforces Round #449 (Div. 1) B. Ithea Plays With Chtholly
- unregister mbean error javax.management.InstanceNotFoundException
- 多线程编程学习::POSIX 多线程基础(一)
- 多项目同质化 gradle配置问题
- Java笔记11
- [学习笔记][Java编程思想]第9章:接口
- POJ-1028 Web Navigation(STL)
- 修改K8S中NodePort方式暴露服务的端口的默认范围(30000-32767)的方法