dubbo refrence bean(服務引用)
阿新 • • 發佈:2017-09-16
研究 temp exce proxy sim 存儲 fig ins tcl
在xml上寫一個dubbo標簽就可以把遠程的服務引用到本地使用:
<dubbo:service interface="com.test.dubbo.service.BuyFoodService" ref="buyFoodService"/>
既然用spring那就是Schema了,dubbo中自定義了Schema,在DubboNamespaceHandler中:
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));spring 中繼承BeanDefinitionParser 實現自定義解析xml的規則。DubboBeanDefinitionParser內實現了解析。 最終要生成一個對應class的BeanDefinition。BeanDefinition在spring中時bean的數據源。 各個標簽對應的pojo:
publicReference標簽的屬性文檔:http://dubbo.io/user-guide/reference-xmlconf/dubbo-reference.html ReferenceBean的繼承結構: 直接看afterPropertiesSet()方法,在spring中初始化好bean後會執行這個方法,方法中會註入各個組件: ConsumerConfig,ApplicationConfig,List<RegistryConfig>,MonitorConfig,ModuleConfig,然後是這段代碼: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)); }
Boolean b = isInit(); if (b == null && getConsumer() != null) { b = getConsumer().isInit(); } if (b != null && b.booleanValue()) { getObject(); }
這個init就是前面設置reference標簽時的一個可選屬性,如果我們設置true,那麽在執行afterPropertiesSet()的時候就會執行到這個getObject()方法。
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); 首先protcolc鏟射高一個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,並沒有判斷,不是很好,如果沒有構造函數需要拋出異常打印出友好的提示可能會好點。這裏就是把JavassistProxyFactory生成好的proxy作為參數生成出裝飾實例。如此在這個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容器中實際指向的是一個封裝好的代理。
dubbo refrence bean(服務引用)