dubbo的服務訂閱能夠經過2種方式: 1)經過xml文件的標籤<dubbo:reference /> ;2)經過註解@DubboReference。
這2種服務訂閱在使用上基本沒區別,由於標籤<dubbo:reference />上的屬性字段均可以在註解@DubboReference上對應的找到。通常使用XML的配置文件方式來訂閱服務。
可是這2種的源碼實現上有必定的區別和公用。
首先,這2種的實現最終都是調用ReferenceBean#get()方法來產生代理對象和訂閱服務。因此ReferenceBean#get()方法是咱們分析的重點。
其次,標籤<dubbo:reference />的實現方式是經過Spring自定義的標籤來實現的,當一個<dubbo:reference />標籤被DubboBeanDefinitionParser處理轉化後,會變成一個RootBeanDefinition,接着註冊到Spring容器中。而這個RootBeanDefinition對應的類就是ReferenceBean,這個ReferenceBean 實現了工廠Bean接口FactoryBean,因此在具體建立這個Bean得對象時候,會調用FactoryBean#getObject()來返回具體類對象。恰好這個ReferenceBean的getObject()方法就是調用ReferenceBean#get()來返回具體引用服務類型對象和訂閱服務。
再次,註解@DubboReference的實現方式則有所不一樣,它是經過BeanPostProcessor來實現的。通常咱們把註解@DubboReference打在某個被Spring託管的類的成員變量上,例如:html
@Component("demoServiceComponent") public class DemoServiceComponent implements DemoService { @DubboReference(check = false, mock = "true" ,version = "2.0.0") private DemoService demoService; ...... }
因此能夠知道@DubboReference註解和@Resource,@Autowired等注入註解相似,都是注入一個DemoService這種類型的對象。
而Dubbo框架正是經過本身編寫相似於AutowiredAnnotationBeanPostProcessor的處理器ReferenceAnnotationBeanPostProcessor,經過這個處理器來處理@DubboReference註解。讀者能夠2者結合起來看。spring
經過xml配置的方式的解析沒有太多的價值,因此咱們直接分析註解的方式。接下來咱們分析Spring擴展點和dubbo框架的相結合內容。緩存
在遠程服務引用的解析前,咱們須要先了解下Spring的一個特殊的BeanPostProcessor,即
InstantiationAwareBeanPostProcessor,該後置處理器主要有三個做用:
1)、Bean實例化前的一個回調:postProcessBeforeInstantiation。
2)、Bean實例化後屬性顯式填充和自動注入前的回調:postProcessAfterInstantiation
3)、給Bean對象屬性自動注入的機會:postProcessPropertyValues
顯而易見,被打上@DubboReference 註解的屬性值注入,就是利用了這個後置處理器的第三個做用(給Bean對象屬性自動注入的機會postProcessPropertyValues)。就是在這個方法內處理Bean對象屬性成員的注入。
有了以上Spring擴展點的基礎,咱們來看下AbstractAnnotationBeanPostProcessor的postProcessPropertyValues。
由於ReferenceAnnotationBeanPostProcessor繼承了AbstractAnnotationBeanPostProcessor.app
public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { /** * 查找bean 下須要注入的相關成員(包括成員變量和方法,即被@DubboReference標註的成員,並把這些這些屬性集合封裝爲一個對象InjectionMetadata,) * InjectionMetadata 對象內部for 循環,一次注入相關的屬性值。 */ InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName() + " dependencies is failed", ex); } return pvs; }
這個方法的實現和上面的基礎鋪墊內容一致。首先第一步就是經過findInjectionMetadata()獲得查找須要注入的相關成員。接着經過Spring內置的對象InjectionMetadata來注入bean的相關屬性。這裏須要注意點,metdata對象封裝了該bean 須要注入的全部屬性成員,在inject內部for循環一致注入。查看代碼相似以下:框架
public void inject(Object target, String beanName, PropertyValues pvs){ Collection<InjectedElement> elementsToIterate = (this.checkedElements != null ? this.checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { for (InjectedElement element : elementsToIterate) { element.inject(target, beanName, pvs); } } }
經過第一個問題,咱們知道當一個Spring Service的對象引用了遠程對象(經過註解@DubboReference),是經過postProcessPropertyValues注入的。接着咱們須要找到它是如何須要注入的屬性成員。ide
/** * 找到相關的須要注入的成員元信息,並封裝爲InjectionMetadata * * @param beanName 當前須要被注入的 beanName * @param clazz 當前須要被注入的 類的Class * @param pvs 當前 須要被注入bean 的參數 * @return */ private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) { // Fall back to class name as cache key, for backwards compatibility with custom callers. //獲取緩存key,默認爲beanName,不然爲className String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // Quick check on the concurrent map first, with minimal locking. //從緩存中拿,若是未null 或者注入屬性元數據所在的目標類和當前須要注入屬性的bean的類型不一致時,須要重寫獲取 AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); //雙層判斷鎖定 if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { //原來的目標類不一致,先clear下參數屬性,但排除須要的參數pvs metadata.clear(pvs); } try { metadata = buildAnnotatedMetadata(clazz); //經過須要注入的類的字節碼clazz,獲得須要被注入的屬性的元信息。 this.injectionMetadataCache.put(cacheKey, metadata); //放入緩存。 } catch (NoClassDefFoundError err) { throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() + "] for annotation metadata: could not find class that it depends on", err); } } } } return metadata; }
該註釋已經很是清楚了,其實就是經過buildAnnotatedMetadata()方法構建注入的元數據信息,而後放入緩存injectionMetadataCache中。而buildAnnotatedMetadata()方法的以下:post
private AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) { /** * * 一、查找 須要注入的成員的元信息 */ Collection<AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass); /** * * 二、查找 須要注入的方法的元信息 */ Collection<AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass); /** * * 組合返回元信息 */ return new AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements); }
其中findFieldAnnotationMetadata()和findAnnotatedMethodMetadata()方法其實就是分別在clazz上的成員和方法上找是否有被@DubboReference打標的屬性。ui
Dubbo框架爲了實現屬性的注入,分別定義了2個注入類,一個AnnotatedFieldElement和一個AnnotatedMethodElement。很顯然,一個是整對成員,一個整對方法。這個2個類都繼承了
Spring的InjectionMetadata.InjectedElement,而後實現inject方法。接着咱們來看下
這2個類的inject 是如何實現:this
public class AnnotatedFieldElement extends InjectionMetadata.InjectedElement { private final Field field; private final AnnotationAttributes attributes; private volatile Object bean; protected AnnotatedFieldElement(Field field, AnnotationAttributes attributes) { super(field, null); this.field = field; this.attributes = attributes; } @Override protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { Class<?> injectedType = field.getType(); Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this); ReflectionUtils.makeAccessible(field); field.set(bean, injectedObject); } }
private class AnnotatedMethodElement extends InjectionMetadata.InjectedElement { private final Method method; private final AnnotationAttributes attributes; private volatile Object object; protected AnnotatedMethodElement(Method method, PropertyDescriptor pd, AnnotationAttributes attributes) { super(method, pd); this.method = method; this.attributes = attributes; } @Override protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { Class<?> injectedType = pd.getPropertyType(); Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this); ReflectionUtils.makeAccessible(method); method.invoke(bean, injectedObject); } }
其實就是經過反射,把須要的屬性對象注入進來。成員屬性就經過field.set()。而方法就經過method.invoke().那麼注入的injectedObject對象是如何獲取的呢。從上面的inject()方法知道,經過調用getInjectedObject()方法來實現,而該方法其實只是從緩存injectedObjectsCache中獲取注入的對象,若是不存在,就調用doGetInjectedBean().接着放入緩存中。
doGetInjectedBean()方法的做用見註釋:代理
/** * * * 該方法是個模板方法,用來獲得一個須要注入類型的的對象。 * * @param attributes ReferenceBean註解屬性 * @param bean 須要被注入的對象,通常是Spring Bean * @param beanName 須要被注入的對象名 * @param injectedType 注入對象的類型 * @param injectedElement 注入對象描述元信息 * @return * @throws Exception */ @Override protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType, InjectionMetadata.InjectedElement injectedElement) throws Exception { /** * The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext} */ /** * * 獲得須要被注入的對象的BeanName,生成規則默認是,查看ServiceBeanNameBuilder.build() * ServiceBean:${interfaceName}:${version}:${group} */ String referencedBeanName = buildReferencedBeanName(attributes, injectedType); /** * The name of bean that is declared by {@link Reference @Reference} annotation injection */ /** * 獲得引用對象@ReferenceBean的BeanName , * 若是有Id 就是Id,沒有經過generateReferenceBeanName()產生,產生的類名以下: * @Reference(${attributes})${interface} */ String referenceBeanName = getReferenceBeanName(attributes, injectedType); /** * 構建一個ReferenceBean 對象,若是不存在的話。 * */ ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType); /** * 判斷是否爲本地ServiceBean,通常都是遠程引用dubbo服務。 */ boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes); /** * 而後註冊referenceBean */ registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType); /** * 把referenceBean 放入緩存中。 */ cacheInjectedReferenceBean(referenceBean, injectedElement); /** * * 經過referenceBean 建立動態代理建立一個injectedType類型的對象。核心,建立一個代理對象,用來表明須要引用遠程的Service服務 */ return getOrCreateProxy(referencedBeanName, referenceBean, localServiceBean, injectedType); }
上面內部方法調用都比較簡單,邏輯也很是清楚,最終是經過getOrCreateProxy()方法來建立一個遠程的代理對象,而後經過InjectedElement.inject注入該代理對象。
接着咱們最好看下getOrCreateProxy()方法。
private Object getOrCreateProxy(String referencedBeanName, ReferenceBean referenceBean, boolean localServiceBean, Class<?> serviceInterfaceType) { /** * * 若是是本地就有服務Bean的話。 */ if (localServiceBean) { // If the local @Service Bean exists, build a proxy of Service //經過Proxy.newProxyInstance建立一個新的代理對象,在內部經過applicationContext獲取本地Service便可。 return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType}, newReferencedBeanInvocationHandler(referencedBeanName)); } else { //若是是遠程Service,判斷被引用的服務referencedBeanName是否已經存在在applicationContext上,是的話,直接暴露服務, // 基本上不太可能,由於在前面已經判斷了被引用的服務Bean在遠程,因此這裏僅僅是爲了防止localServiceBean判斷錯誤。 exportServiceBeanIfNecessary(referencedBeanName); // If the referenced ServiceBean exits, export it immediately //接着直接經過get()方法來建立一個代理,這get操做就會引入dubbo服務的訂閱等相關內容。 return referenceBean.get(); } }
能夠看到最終調用了referenceBean.get()方法來建立一個遠程本地代理對象。referenceBean.get()在下一個章節分析。 到這裏,咱們已經分析了@DubboReference註解的處理過程,而後知道了referenceBean.get()在Spring的postProcessPropertyValues擴展點上被調用。