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。

消费者的代理创建就此结束。





原创粉丝点击