,线上出事故。前天晚上上线了,还发生了一件很有意思的事情。昨天复习,今天分享。晚上,我负责的系统和收银系统同时上线(用的是dubbo)。然后惊人的事情发生了。收银系统给我的接口注入了@Reference注解,然后这个接口的实现类其实是空的。其实我们当时并没有找出原因?“重启就好了,毕竟重启大法就好了。”但是为了不给用户充值造成障碍,我们还是要研究下代理对象是怎么为空的。的。《网上dubbo的版本是2.8.9,注意包名是(com.alibaba)》为了方便大家理解我说的,我简单说一下RPC框架的执行过程。Server向Registry注册服务信息,Client从Registry中拉取Server信息。客户端通过代理对象(ClientStub)发送网络请求,服务器通过代理对象(ServerStub)执行本地方法。在网络传输过程中有一个编码、解码和序列化的过程。“在Dubbo中,ClientStub和ServerStub都是Invoker对象。”继续,注入的接口实现类可以为空吗?我只是看了一下他写的代码,只用了一个@Reference注解,没有设置任何属性。@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.FIELD,ElementType.METHOD,ElementType.ANNOTATION_TYPE})public@interfaceReference{//省略其他属性booleancheck()defaulttrue;}然后check=true,即没有serviceprovided当使用后者时,服务消费者无法正常启动,因为会抛出IllegalStateException。既然可以正常启动,代理对象已经正常创建,不能为null//2.8.9版本//ReferenceConfig#createProxyBooleanc=check;if(c==null&&consumer!=null){c=consumer.isCheck();}if(c==null){c=true;//defaulttrue}if(c&&!invoker.isAvailable()){thrownewIllegalStateException("Failedtocheckthestatusoftheservice"+interfaceName+".Noprovideravailablefortheservice"+(group==null?"":group+"/")+interfaceName+(version==null?"":":"+version)+"fromtheurl"+invoker.getUrl()+"totheconsumer"+NetUtils.getLocalHost()+"usedubboversion"+Version。getVersion());}"那我同事说,会不会是客户端先启动了,代理对象是空的,因为没有服务提供者?"我说不可能,先启动客户端,check属性为true,不可能启动成功!再说了,每次上线,新服务正常启动后,旧服务就会关闭,服务商肯定会有。“为什么会这样,我实在是不明白,只能google“@Referenceinjectionobjectisnull””答案基本一样,没有服务提供者,代理对象为空,设置check属性即可@Reference到False就足够了。至于原因,没有一篇文章说“下一步要在网上验证方法”。先启动生产者,再启动消费者。正常调用,先启动消费者(check=true),再启动生产者。代理对象为空。完美再现。先启动消费者(check=false),再启动生产者,正常调用。学习dubbo时用的例子,测试了一段时间。dubbo的版本是2.7.3。请注意,包名称是(org.apache)。先启动生产者,再启动消费者。正常调用,先启动消费者(check=true),此时没有生产者。如果启动失败,先启动消费者(check=false),再启动生产者,正常调用“这符合我的想法”。真相大白。由于@Reference注入的对象为null,说明SpringBean生命周期中有一个属性赋值阶段。我们来分析一下@Reference注解的注入逻辑,和@Autowired、@Resource注解的注入逻辑基本类似。当你加入Dubbo的springbootstarter时,ReferenceAnnotationBeanPostProcessor会被注入到容器中。看一下这个类的继承关系,其中最重要的部分你只需要知道这个类重写了InstantiationAwareBeanPostProcessor#postProcessPropertyValues(这个方法在以后的版本中会被postProcessProperties方法代替),使用的就是这个方法属性赋值见上面的Bean生命周期图bean.getClass(),pvs);try{metadata.inject(bean,beanName,pvs);}catch(BeanCreationExceptionex){throwex;}catch(Throwableex){thrownewBeanCreationException(beanName,"Injectionof@Referencedependenciesfailed",ex);}returnpvs;}}然后执行到ReferenceFieldElement#inject方法,obj@Reference引入的ect会被转换为ReferenceBeanprivateclassReferenceFieldElementextendsInjectionMetadata.InjectedElement{@OverrideNameanbeperjectedvoValuespvs)throwsThrowable{Class>referenceClass=field.getType();//获取referenceBean的逻辑在这里referenceBean=buildReferenceBean(reference,referenceClass);ReflectionUtils.makeAccessible(field);//通过反射域注入对象。set(bean,referenceBean.getObject());}}一系列方法调用后执行到下面的方法//AbstractAnnotationConfigBeanBuilder#buildpublicfinalBbuild()throwsException{checkDependencies();Bbean=doBuild();configureBean(bean);if(logger.isInfoEnabled()){logger.info(bean+"hasbeenbuilt.");}returnbean;}此时log会打印ReferenceBean对象,它继承了AbstractConfig,所以会执行AbstractConfig#toString方法publicabstractclassAbstractConfigimplementsSerializable{@OverridepublicStringtoString(){try{StringBuilderbuf=newStringBuilder();buf.append("
