溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

dubbo之@Reference注解有什么作用

發(fā)布時(shí)間:2023-03-21 14:18:07 來(lái)源:億速云 閱讀:76 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“dubbo之@Reference注解有什么作用”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“dubbo之@Reference注解有什么作用”文章能幫助大家解決問(wèn)題。

    目的

    看看dubbo是怎么給加了@Reference注解的屬性注入invoker實(shí)例,為什么有時(shí)候加了@Reference注解的屬性會(huì)是null。

    ReferenceAnnotationBeanPostProcessor

    看到這個(gè)名字,就很容易知道,是專(zhuān)門(mén)針對(duì)@Reference注解的后置處理。

    ReferenceAnnotationBeanPostProcessor的代碼比較多,下面列一下比較重要的內(nèi)容。

    public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
            implements MergedBeanDefinitionPostProcessor, PriorityOrdered, ApplicationContextAware, BeanClassLoaderAware,
            DisposableBean {
        // 緩存加了@Referece注解的元數(shù)據(jù)信息,key是bean的名稱(chēng)或者類(lèi)名,value是加了@Reference的屬性和方法
        private final ConcurrentMap<String, ReferenceInjectionMetadata> injectionMetadataCache =
                new ConcurrentHashMap<String, ReferenceInjectionMetadata>(256);
    	// 緩存new過(guò)的ReferenceBean,相同的key,只會(huì)產(chǎn)生一個(gè)ReferenceBean
        private final ConcurrentMap<String, ReferenceBean<?>> referenceBeansCache =
                new ConcurrentHashMap<String, ReferenceBean<?>>();
    
    	// spring設(shè)置屬性到bean之前調(diào)用該方法
        @Override
        public PropertyValues postProcessPropertyValues(
                PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    		// 根據(jù)bean的類(lèi)型,獲取需要注入的元數(shù)據(jù)信息
            InjectionMetadata metadata = findReferenceMetadata(beanName, bean.getClass(), pvs);
            try {
                // 注入對(duì)象
                metadata.inject(bean, beanName, pvs);
            } catch (BeanCreationException ex) {
                throw ex;
            } catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Injection of @Reference dependencies failed", ex);
            }
            return pvs;
        }
    
      private InjectionMetadata findReferenceMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
            // 如果是自定義的消費(fèi)者,沒(méi)有beanName,退化成使用類(lèi)名作為緩存key
            String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
            // 雙重檢查,判斷是否需要刷新注入信息
            ReferenceInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
            // 判斷是否需要刷新
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            	// 第一次判斷為需要刷新,則鎖住injectionMetadataCache對(duì)象
                synchronized (this.injectionMetadataCache) {
                	// 再次判斷是否需要刷新
                    metadata = this.injectionMetadataCache.get(cacheKey);
                    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                    	// 需要刷新,而且原來(lái)緩存的信息不為空,清除緩存信息
                        if (metadata != null) {
                            metadata.clear(pvs);
                        }
                        try {
                        	// 生成新的元數(shù)據(jù)信息
                            metadata = buildReferenceMetadata(clazz);
                            // 放入緩存
                            this.injectionMetadataCache.put(cacheKey, metadata);
                        } catch (NoClassDefFoundError err) {
                            throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
                                    "] for reference metadata: could not find class that it depends on", err);
                        }
                    }
                }
            }
            return metadata;
        }
    
        private ReferenceInjectionMetadata buildReferenceMetadata(final Class<?> beanClass) {
        	// 查找加了@Reference注解的屬性
            Collection<ReferenceFieldElement> fieldElements = findFieldReferenceMetadata(beanClass);
            // 查找加了@Reference注解的屬性
            // !!!!@Reference還能加到方法上!!!還真沒(méi)試過(guò)
            // 不過(guò)這個(gè)不關(guān)心,只關(guān)注屬性的
            Collection<ReferenceMethodElement> methodElements = findMethodReferenceMetadata(beanClass);
            return new ReferenceInjectionMetadata(beanClass, fieldElements, methodElements);
        }
    
        private List<ReferenceFieldElement> findFieldReferenceMetadata(final Class<?> beanClass) {
    		
    		// 保存加了@Reference注解的屬性列表
            final List<ReferenceFieldElement> elements = new LinkedList<ReferenceFieldElement>();
    
            ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
                @Override
                public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
    				// 獲取屬性上的@Reference注解
                    Reference reference = getAnnotation(field, Reference.class);
    				// 如果存在@Reference注解
                    if (reference != null) {
    					// 不支持靜態(tài)屬性的注入
                        if (Modifier.isStatic(field.getModifiers())) {
                            if (logger.isWarnEnabled()) {
                                logger.warn("@Reference annotation is not supported on static fields: " + field);
                            }
                            return;
                        }
    					// 添加到隊(duì)列里
                        elements.add(new ReferenceFieldElement(field, reference));
                    }
    
                }
            });
    
            return elements;
    
        }

    ReferenceInjectionMetadata

    dubbo在ReferenceAnnotationBeanPostProcessor里定義了一個(gè)私有的子類(lèi)

    ReferenceInjectionMetadata繼承spring定義的InjectionMetadata類(lèi)。

    之所以需要自定義ReferenceInjectionMetadata類(lèi),是因?yàn)閐ubbo的@Reference注解可以使用在屬性和方法上,需要區(qū)分開(kāi)。但是spring定義的InjectionMetadata類(lèi),只支持一個(gè)injectedElements集合,代碼如下:

    public class InjectionMetadata {
    
    	private static final Log logger = LogFactory.getLog(InjectionMetadata.class);
    
    	private final Class<?> targetClass;
    
    	private final Collection<InjectedElement> injectedElements;
    
    	@Nullable
    	private volatile Set<InjectedElement> checkedElements;
    
    
    	public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
    		// 構(gòu)造函數(shù)接收兩個(gè)參數(shù),類(lèi)型,注入的元素
    		this.targetClass = targetClass;
    		this.injectedElements = elements;
    	}
        ......
        ......
        ......
    	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    		Collection<InjectedElement> checkedElements = this.checkedElements;
    		// 優(yōu)先使用checkedElements來(lái)注入,如果checkedElements為空,則直接使用injectedElements(沒(méi)有調(diào)用checkConfigMembers方法,checkedElements會(huì)空)
    		Collection<InjectedElement> elementsToIterate =
    				(checkedElements != null ? checkedElements : this.injectedElements);
    		if (!elementsToIterate.isEmpty()) {
    		    // 循環(huán)遍歷所有待注入的元素
    			for (InjectedElement element : elementsToIterate) {
    				if (logger.isTraceEnabled()) {
    					logger.trace("Processing injected element of bean '" + beanName + "': " + element);
    				}
    				// 調(diào)用注入方法
    				element.inject(target, beanName, pvs);
    			}
    		}
    	}

    基于這個(gè)原因,dubbo定義了ReferenceInjectionMetadata類(lèi),代碼如下:

     private static class ReferenceInjectionMetadata extends InjectionMetadata {
    
            private final Collection<ReferenceFieldElement> fieldElements;
    
            private final Collection<ReferenceMethodElement> methodElements;
    
    
            public ReferenceInjectionMetadata(Class<?> targetClass, Collection<ReferenceFieldElement> fieldElements,
                                              Collection<ReferenceMethodElement> methodElements) {
                // 構(gòu)造函數(shù)接收3個(gè)參數(shù),類(lèi)型,待注入的屬性元素,待注入的方法元素
                // 把fieldElements和methodElements的內(nèi)容合并,作為InjectionMetadata的injectedElements
                super(targetClass, combine(fieldElements, methodElements));
                this.fieldElements = fieldElements;
                this.methodElements = methodElements;
            }
    
            private static <T> Collection<T> combine(Collection<? extends T>... elements) {
                List<T> allElements = new ArrayList<T>();
                for (Collection<? extends T> e : elements) {
                    allElements.addAll(e);
                }
                return allElements;
            }
    
            public Collection<ReferenceFieldElement> getFieldElements() {
                return fieldElements;
            }
    
            public Collection<ReferenceMethodElement> getMethodElements() {
                return methodElements;
            }
        }

    代碼很簡(jiǎn)單,入?yún)⒆優(yōu)?個(gè),屬性和方法列表區(qū)分開(kāi),然后把兩者合并起來(lái),調(diào)用父類(lèi)的構(gòu)造函數(shù),用fieldElements保存屬性列表,用methodElements保存方法列表。

    ReferenceFieldElement

    屬性注入實(shí)際發(fā)生在ReferenceFieldElement類(lèi),代碼如下:

     private class ReferenceFieldElement extends InjectionMetadata.InjectedElement {
    
            private final Field field;
    
            private final Reference reference;
    
            private volatile ReferenceBean<?> referenceBean;
    
            // 構(gòu)造函數(shù)會(huì)傳入要設(shè)置的Field對(duì)象,Reference注解對(duì)象
            protected ReferenceFieldElement(Field field, Reference reference) {
                super(field, null);
                this.field = field;
                this.reference = reference;
            }
    
            @Override
            protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    
                Class<?> referenceClass = field.getType();
    			// 構(gòu)建ReferenceBean對(duì)象
                referenceBean = buildReferenceBean(reference, referenceClass);
    			// 將屬性設(shè)置為可訪(fǎng)問(wèn)的
                ReflectionUtils.makeAccessible(field);
    			// 給Field對(duì)象設(shè)置屬性
                field.set(bean, referenceBean.getObject());
    
            }
    
        }

    ReferenceMethodElement

    方法注入實(shí)際發(fā)生在ReferenceMethodElement類(lèi),代碼如下:

    private class ReferenceMethodElement extends InjectionMetadata.InjectedElement {
    
            private final Method method;
    
            private final Reference reference;
    
            private volatile ReferenceBean<?> referenceBean;
    
            protected ReferenceMethodElement(Method method, PropertyDescriptor pd, Reference reference) {
                super(method, pd);
                this.method = method;
                this.reference = reference;
            }
    
            @Override
            protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    			// 獲取類(lèi)型
                Class<?> referenceClass = pd.getPropertyType();
    			// 構(gòu)建ReferenceBean對(duì)象
                referenceBean = buildReferenceBean(reference, referenceClass);
    			// 將方法設(shè)置為可訪(fǎng)問(wèn)
                ReflectionUtils.makeAccessible(method);
    			// 把referenceBean生成的對(duì)象作為入?yún)?,調(diào)用bean對(duì)象的method方法
    			// 看到這里,就能明白,@Reference也可以加在那些setXXX方法上
    			// 例如有個(gè)userService屬性,有個(gè)setUserService方法,可以在setUserService方法上加@Reference注解
                method.invoke(bean, referenceBean.getObject());
    
            }
    
        }

    為什么加了@Reference注解的屬性是null

    從上面的代碼分析,可以知道屬性的注入,是靠ReferenceAnnotationBeanPostProcessor后置處理來(lái)觸發(fā),往filed設(shè)置值。

    如果這一過(guò)程中,發(fā)生異常,導(dǎo)致沒(méi)有成功為field設(shè)置值,則加了@Referencce的屬性就會(huì)一直是null。

    2020-07-30 17:00:00.013  WARN 13092 --- [           main] com.alibaba.dubbo.config.AbstractConfig  : []  [DUBBO] null, dubbo version: 2.6.2, current host: 10.0.45.150

    java.lang.reflect.InvocationTargetException: null
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.alibaba.dubbo.config.AbstractConfig.toString(AbstractConfig.java:474)
        at java.lang.String.valueOf(String.java:2994)
        at java.lang.StringBuilder.append(StringBuilder.java:131)
        at com.alibaba.dubbo.config.spring.beans.factory.annotation.AbstractAnnotationConfigBeanBuilder.build(AbstractAnnotationConfigBeanBuilder.java:79)
        at com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.buildReferenceBean(ReferenceAnnotationBeanPostProcessor.java:385)
        at com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.access$100(ReferenceAnnotationBeanPostProcessor.java:65)
        at com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor$ReferenceFieldElement.inject(ReferenceAnnotationBeanPostProcessor.java:363)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
        at com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.postProcessPropertyValues(ReferenceAnnotationBeanPostProcessor.java:92)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1400)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1247)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1325)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1171)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
        at com.xdchen.bp.award.api.server.Application.main(Application.java:20)
    Caused by: java.lang.IllegalStateException: Failed to check the status of the service com.xdchen.searchplatform.searcher.protocol.SearcherService. No provider available for the service com.xdchen.searchplatform.searcher.protocol.SearcherService:1.0.0111 from the url zookeeper://zk1.esf.fdd:2181/com.alibaba.dubbo.registry.RegistryService?application=award.ddxf.bp.fdd&default.reference.filter=traceIdConsumer,default,consumerCatFilter&default.timeout=6000&dubbo=2.6.2&interface=com.xdchen.searchplatform.searcher.protocol.SearcherService&methods=search,searchByBytes,multiSearch,scrollIndex,searchByHttp,searchByIds,multiSearchByBytes&organization=fangdd&owner=chenxudong&pid=13092&register.ip=10.0.45.150&revision=3.8.0&side=consumer&timeout=5000&timestamp=1596099599500&version=1.0.0111 to the consumer 10.0.45.150 use dubbo version 2.6.2
        at com.alibaba.dubbo.config.ReferenceConfig.createProxy(ReferenceConfig.java:422)
        at com.alibaba.dubbo.config.ReferenceConfig.init(ReferenceConfig.java:333)
        at com.alibaba.dubbo.config.ReferenceConfig.get(ReferenceConfig.java:163)
        at com.alibaba.dubbo.config.spring.ReferenceBean.getObject(ReferenceBean.java:66)
        ... 42 common frames omitted

    2020-07-30 17:00:00.014  INFO 13092 --- [           main] c.a.d.c.s.b.f.a.ReferenceBeanBuilder     : [] <dubbo:reference singleton="true" interface="com.xdchen.searchplatform.searcher.protocol.SearcherService" uniqueServiceName="com.xdchen.searchplatform.searcher.protocol.SearcherService:1.0.0111" generic="false" version="1.0.0111" timeout="5000" id="com.xdchen.searchplatform.searcher.protocol.SearcherService" /> has been built.

    看這一段錯(cuò)誤日志,當(dāng)SearcherService沒(méi)有任何provider啟動(dòng)的時(shí)候調(diào)用ReferenceBean.getObject方法,就會(huì)拋IllegalStateException異常,設(shè)置屬性失敗。

    網(wǎng)上很多說(shuō),遇到加@Reference注解的屬性為null的,應(yīng)該就是這個(gè)情況。

    什么情況會(huì)拋No provider的IllegalStateException異常

    ReferenceAnnotationBeanPostProcessor.buildReferenceBean

       private ReferenceBean<?> buildReferenceBean(Reference reference, Class<?> referenceClass) throws Exception {
    
            String referenceBeanCacheKey = generateReferenceBeanCacheKey(reference, referenceClass);
    
            ReferenceBean<?> referenceBean = referenceBeansCache.get(referenceBeanCacheKey);
    
            if (referenceBean == null) {
    
                ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
                        .create(reference, classLoader, applicationContext)
                        .interfaceClass(referenceClass);
    			// 這里報(bào)錯(cuò)
                referenceBean = beanBuilder.build();
    
                referenceBeansCache.putIfAbsent(referenceBeanCacheKey, referenceBean);
    
            }
    
            return referenceBean;
    
        }

    buildReferenceBean方法調(diào)用ReferenceBeanBuilder.build報(bào)錯(cuò)

    ReferenceBeanBuilder.build

    ReferenceBeanBuilder.build方法是它的父類(lèi)AbstractAnnotationConfigBeanBuilder的

     public final B build() throws Exception {
    
            checkDependencies();
    		
            B bean = doBuild();
    
            configureBean(bean);
            
            if (logger.isInfoEnabled()) {
            	// 這里報(bào)錯(cuò)
                logger.info(bean + " has been built.");
            }
    
            return bean;
    
        }

    ReferenceBeanBuilder.doBuild

    ReferenceBeanBuilder重寫(xiě)了doBuild方法,返回ReferenceBean對(duì)象

        @Override
        protected ReferenceBean doBuild() {
            return new ReferenceBean<Object>();
        }

    所以,問(wèn)題是出在了ReferenceBean.toString方法上

    AbstractConfig.toString

    ReferenceBean并沒(méi)有重寫(xiě)toString方法,但他的根父類(lèi)是AbstractConfig,看錯(cuò)誤日志,可以看到這個(gè):

    at com.alibaba.dubbo.config.AbstractConfig.toString(AbstractConfig.java:474)

    AbstractConfig.toString代碼如下:

     @Override
        public String toString() {
            try {
                StringBuilder buf = new StringBuilder();
                buf.append("<dubbo:");
                buf.append(getTagName(getClass()));
                Method[] methods = getClass().getMethods();
                // 拿到當(dāng)前類(lèi)的所有方法
                for (Method method : methods) {
                    try {
                        String name = method.getName();
                        // 過(guò)濾剩下get和is開(kāi)頭的方法,但不包括getClass、get和is
                        if ((name.startsWith("get") || name.startsWith("is"))
                                && !"getClass".equals(name) && !"get".equals(name) && !"is".equals(name)
                                && Modifier.isPublic(method.getModifiers())
                                && method.getParameterTypes().length == 0
                                && isPrimitive(method.getReturnType())) {
                            int i = name.startsWith("get") ? 3 : 2;
                            String key = name.substring(i, i + 1).toLowerCase() + name.substring(i + 1);
                            // 反射獲取方法返回值,拼接字符串
                            // 就是這里報(bào)空指針
                            Object value = method.invoke(this, new Object[0]);
                            if (value != null) {
                                buf.append(" ");
                                buf.append(key);
                                buf.append("=\"");
                                buf.append(value);
                                buf.append("\"");
                            }
                        }
                    } catch (Exception e) {
                        logger.warn(e.getMessage(), e);
                    }
                }
                buf.append(" />");
                return buf.toString();
            } catch (Throwable t) {
                logger.warn(t.getMessage(), t);
                return super.toString();
            }
        }

    ReferenceBean.getObjet

    ReferenceBean類(lèi)實(shí)現(xiàn)類(lèi)FactoryBean接口,實(shí)現(xiàn)了getObject方法,getObject方法滿(mǎn)足get開(kāi)頭的條件,會(huì)被AbstractConfig.toString方法調(diào)用到

    public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
        @Override
        public Object getObject() throws Exception {
            return get();
        }
        
        public synchronized T get() {
            if (destroyed) {
                throw new IllegalStateException("Already destroyed!");
            }
            if (ref == null) {
                init();
            }
            return ref;
        }
    
        private void init() {
            if (initialized) {
                return;
            }
            initialized = true;
            ......
            ......
            ref = createProxy(map);
            ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
            ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
        }
    
        private T createProxy(Map<String, String> map) {
            ......
    		......
            Boolean c = check;
            if (c == null && consumer != null) {
                c = consumer.isCheck();
            }
            if (c == null) {
                c = true; // default true
            }
            if (c && !invoker.isAvailable()) {
                throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
            }
            if (logger.isInfoEnabled()) {
                logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
            }
            // create service proxy
            return (T) proxyFactory.getProxy(invoker);
        }

    省略了大部分代碼,只保留了比較重要的,調(diào)用getObject方法,會(huì)判斷是否初始化過(guò),如果初始化過(guò),直接返回ref;如果沒(méi)有初始化,則會(huì)進(jìn)行初始化,然后調(diào)用createProxy方法來(lái)創(chuàng)建代理,如果我們沒(méi)有配置consumer的check或者check=true,則會(huì)檢查invoker對(duì)象的可用性“invoker.isAvailable()”,如果不可用,就會(huì)拋IllegalStateException異常。

    避免@Reference注解的屬性為null

    配置消費(fèi)者的檢查為false,即@Reference(check=false)

    ReferenceBean.getObject調(diào)用時(shí)機(jī)的猜測(cè)

    看的ReferenceFieldElement.inject方法,很容易以為IllegalStateException是在 “field.set(bean, referenceBean.getObject());”這一行報(bào)錯(cuò)的,但實(shí)際上是在 “referenceBean = buildReferenceBean(reference, referenceClass);”

     @Override
            protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    
                Class<?> referenceClass = field.getType();
    			// 構(gòu)建ReferenceBean對(duì)象
                referenceBean = buildReferenceBean(reference, referenceClass);
    			// 將屬性設(shè)置為可訪(fǎng)問(wèn)的
                ReflectionUtils.makeAccessible(field);
    			// 給Field對(duì)象設(shè)置屬性
                field.set(bean, referenceBean.getObject());
    
            }

    為什么要在AbstractConfig.toString就調(diào)用了getObject方法,觸發(fā)報(bào)錯(cuò)呢?

    如果AbstractConfig.toString過(guò)濾掉getObject方法,會(huì)發(fā)生什么事情呢?

    InjectionMetadata.inject方法是遍歷checkedElements列表,挨個(gè)調(diào)用element.inject方法。

    如果AbstractConfig.toString過(guò)濾掉getObject方法,則首次調(diào)用ReferenceBean.getObject方法是在“field.set(bean, referenceBean.getObject());”。異常沒(méi)有被catch住,checkedElements列表的遍歷會(huì)被打斷。

    會(huì)出現(xiàn)這樣的情況,有一個(gè)bean需要注入5個(gè)代理對(duì)象,但是調(diào)用第一個(gè)ReferenceBean.getObject的時(shí)候拋異常,則注入行為被中斷,另外4個(gè)屬性也沒(méi)有被注入。

    關(guān)于“dubbo之@Reference注解有什么作用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI