Dubbo消费者代理的创建

在消费者端,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。

 

 

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

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值