dubbo refrence bean(服務引用)

在xml上寫一個dubbo標籤就能夠把遠程的服務引用到本地使用:html

<dubbo:reference id="buyFoodService" interface="com.test.dubbo.service.BuyFoodService"/>

既然用spring那就是Schema了,dubbo中自定義了Schema,在DubboNamespaceHandler中:java

registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
spring 中繼承BeanDefinitionParser 實現自定義解析xml的規則。DubboBeanDefinitionParser內實現瞭解析。
最終要生成一個對應class的BeanDefinition。BeanDefinition在spring中時bean的數據源。
各個標籤對應的pojo:
public void init() {  
     registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(plicationConfig.class, true));  
     registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(duleConfig.class, true));  
     registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(gistryConfig.class, true));  
     registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(nitorConfig.class, true));  
     registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(oviderConfig.class, true));  
     registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(nsumerConfig.class, true));  
     registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(otocolConfig.class, true));  
     registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(rviceBean.class, true));  
     registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ferenceBean.class, false));  
     registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(notationBean.class, true));  
 } 
 
ReferenceBean的繼承結構:
直接看afterPropertiesSet()方法,在spring中初始化好bean後會執行這個方法,方法中會注入各個組件:
ConsumerConfig,ApplicationConfig,List<RegistryConfig>,MonitorConfig,ModuleConfig,而後是這段代碼:
        Boolean b = isInit();
        if (b == null && getConsumer() != null) {
            b = getConsumer().isInit();
        }
        if (b != null && b.booleanValue()) {
            getObject();
        }

這個init就是前面設置reference標籤時的一個可選屬性,若是咱們設置true,那麼在執行afterPropertiesSet()的時候就會執行到這個getObject()方法。spring

  public Object getObject() throws Exception {
        return get();
    }
這個getObject是FactoryBean的實現,這個在spring容器中,FactoryBean跟普通Bean不一樣,經過BeanFactory類的getBean方法直接獲取到的並非該FactoryBean的實例,而是該FactoryBean中方法getObject返回的對象。因此當咱們執行BuyFoodService buyFoodService = (BuyFoodService) context.getBean("buyFoodService");
這樣的代碼是就也會執行到getObject()方法。
getObject()方法中調用的是父類ReferenceConfig的get();而後會調用到init()方法。
 public synchronized T get() {
        if (destroyed) {
            throw new IllegalStateException("Already destroyed!");
        }
        if (ref == null) {
            init();
        }
        return ref;
    }
看這個init方法,寫得不怎麼樣,像膏藥同樣一塊塊散落着一地。先無論,直接看它返回的代碼:
invoker = refprotocol.refer(interfaceClass, url);
// 建立服務代理
return (T) proxyFactory.getProxy(invoker);
首先protcol產生一個invoker,而後把這個invoker代理,來提供使用。
這個proxyFactory和Protocol咱們看到是這樣獲取的:
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
ProxyFactory的代碼:
@SPI("javassist")
public interface ProxyFactory {
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;

    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
Protocol的代碼:
@SPI("dubbo")
public interface Protocol {
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
    void destroy();

}
ProxyFactory用javassist做爲默認實現,Protocol默認用dubbo,因此咱們先關注兩個子類:JavassistProxyFactory和DubboProtocol。
 
1,DubboProtocol的refer方法返回一個DubboInvoker。
2,JavassistProxyFactory把DubboInvoker做爲被代理對象動態產生一個代理類
 
在生成Invoker的時候依次執行的是:ProtocolListenerWrapper,ProtocolFilterWrapper,RegistryProtocol,DubboProtocol。
在這個過程當中須要像註冊中心那信息,組裝出存儲service調用必要信息的實例。其中不少細節,後續本身研究。
 
那麼ProxyFactory是JavassistProxyFactory,其實先執行的是StubProxyFactoryWrapper,在前面文章提到過了這種機制。StubProxyFactoryWrapper的構造函數參數是ProxyFactory,這裏在上篇 dubbo中Listener的實現中也有涉及到。他在getProxy裏作了邏輯。
先了解下Stub(存根),在dubbo中遠程調用一個服務被封裝成一個本地service,通常咱們都是引用接口,就能夠調用到它的方法,實現則在遠程的應用上,但當咱們想在發起遠程請求前作一些事情,好比作ThreadLocal緩存,提早驗證參數,調用失敗後僞造容錯數據。這個就是stub要實現的事情。這段邏輯就在StubProxyFactoryWrapper的getProxy方法裏。咱們來看一下它代碼:
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
    T proxy = proxyFactory.getProxy(invoker);
    if (GenericService.class != invoker.getInterface()) {
       // 查看有沒有stub屬性
        String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY));
        if (ConfigUtils.isNotEmpty(stub)) {
            Class<?> serviceType = invoker.getInterface();
            if (ConfigUtils.isDefault(stub)) {
                if (invoker.getUrl().hasParameter(Constants.STUB_KEY)) {
                    stub = serviceType.getName() + "Stub";
                } else {
                    stub = serviceType.getName() + "Local";
                }
            }
            try {
                Class<?> stubClass = ReflectUtils.forName(stub);
                if (! serviceType.isAssignableFrom(stubClass)) {
                    throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + serviceType.getName());
                }
                try {
                    // 判斷有沒有參數是本service的構造函數,要有這個函數才能可用
                    Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);
                    // 這裏看到是直接使用constructor,並無判斷,不是很好,看起來有問題,實際在解析stub屬性的時候已經作過是否包含指定構造函數,若是沒有責會報錯。因此這裏能夠放心使用,不會空指針。
                    proxy = (T) constructor.newInstance(new Object[] {proxy});
                    //export stub service
                    URL url = invoker.getUrl();
                    if (url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT)){
                        url = url.addParameter(Constants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
                        url = url.addParameter(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
                        try{
                            export(proxy, (Class)invoker.getInterface(), url);
                        }catch (Exception e) {
                            LOGGER.error("export a stub service error.", e);
                        }
                    }
                } catch (NoSuchMethodException e) {
                    throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implemention class " + stubClass.getName(), e);
                }
            } catch (Throwable t) {
                LOGGER.error("Failed to create stub implemention class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
                // ignore
            }
        }
    }
    return proxy;
}

 

返回代理實例,因此在開頭申明的BuyFoodService,在spring容器中實際指向的是一個封裝好的代理。緩存

相關文章
相關標籤/搜索