开发者

dubbo之@Reference注解作用说明

开发者 https://www.devze.com 2023-03-22 10:20 出处:网络 作者: jingxian
目录目的ReferenceAnnotationBeanPostProcessorReferenceInjectionMetadataReferenceFieldElementReferenceMethodElement为什么加了@Reference注解的属性是null什么情况会抛No provider的IllegalStateException异常R
目录
  • 目的
  • ReferenceAnnotationBeanPostProcessor
    • ReferenceInjectionMetadata
    • ReferenceFieldElement
    • ReferenceMethodElement
  • 为什么加了@Reference注解的属性是null
    • 什么情况会抛No provider的IllegalStateException异常
      • ReferenceAnnotationBeanPostProcessor.buildReferenceBean
      • ReferenceBeanBuilder.build
      • ReferenceBeanBuilder.doBuild
      • AbstractConfig.toString
      • ReferenceBean.getObjet
    • 避免@Reference注解的属性为null
      • ReferenceBean.getObject调用时机的猜测
        • 总结

          目的

          看看dubbo是怎么给加了@Reference注解的属性注入invoker实例,为什么有时候加了@Reference注解的属性会是null。

          ReferenceAnnotationBeanPostProcessor

          看到这个名字,就很容易知道,是专门针对@Reference注解的后置处理。

          ReferenceAnnotationBeanPostProcessor的代码比较多,下面列一下比较重要的内容。

          public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
                  implements MergedBeanDefinitionPostProcessor, PriorityOrdered, ApplicationContextAware, BeanClassLoaderAware,
                  DisposableBean {
              // 缓存加了@Referece注解的元数据信息,key是bean的名称或者类名,value是加了@Reference的属性和方法
              private final ConcurrentMap<String, ReferenceInjectionMetadata> injectionMetadataCache =
                      new ConcurrentHashMap<String, ReferenceInjectionMetadata>(256);
          	// 缓存new过的ReferenceBean,相同的key,只会产生一个ReferenceBean
              private final ConcurrentMap<String, ReferenceBean<?>> referenceBeansCache =
                      new ConcurrentHashMap<String, ReferenceBean<?>>();
          
          	// spring设置属性到bean之前调用该方法
              @Override
              public PropertyValues postProcessPropertyValues(
                      PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
          		// 根据bean的类型,获取需要注入的元数据信息
                  InjectionMetadata metadata = findReferenceMetadata(beanName, bean.getClass(), pvs);
                  try {
                      // 注入对象
                      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) {
                  // 如果是自定义的消费者,没有beanName,退化成使用类名作为缓存key
                  String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
                  // 双重检查,判断是否需要刷新注入信息
                  ReferenceInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
                  // 判断是否需要刷新
                  if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                  	// 第一次判断为需要刷新,则锁住injectionMetadataCache对象
                      synchronized (this.injectionMephptadataCache) {
                      	// 再次判断是否需要刷新
                          metadata = this.injectionMetadataCache.get(cacheKey);
                          if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                          	// 需要刷新,而且原来缓存的信息不为空,清除缓存信息
                              if (metadata != null) {
                                  metadata.clear(pvs);
                              }
                              try {
                              	// 生成新的元数据信息
                                  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还能加到方法上!!!还真没试过
                  // 不过这个不关心,只关注属性的
                  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) {
          					// 不支持静态属性的注入
                              if (Modifier.isStatic(field.getModifiers())) {
                                  if (logger.isWarnEnabled()) {
                                      logger.warn("@Reference annotation is not supported on static fields: " + field);
                                  }
                                  return;
                              }
          					// 添加到队列里
                              elements.add(new ReferenceFieldElement(field, reference));
                          }
          
                      }
                  });
          
                  return elements;
          
              }

          大致的流程如下:

          dubbo之@Reference注解作用说明

          ReferenceInjectionMetadata

          dubbo在ReferenceAnnotationBeanPostProcessor里定义了一个私有的子类

          ReferenceInjectionMetadata继承spring定义的InjectionMetadata类。

          之所以需要自定义ReferenceInjectionMetadata类,是因为dubbo的@Reference注解可以使用在属性和方法上,需要区分开。但是spring定义的InjectionMetadata类,只支持一个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) {
          		// 构造函数接收两个参数,类型,注入的元素
          		this.targetClass = targetClass;
          		this.injectedElements = elements;
          	}
              ......
              ......
              ......
          	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
          		Collection<InjectedElement> checkedElements = this.checkedElements;
          		// 优先使用checkedElements来注入,如果checkedElements为空,则直接使用injectedElements(没有调用checkConfigMembers方法,checkedElements会空)
          		Collection<InjectedElement> elementsToIterate =
          				(checkedElements != null ? checkedElements : this.injectedElements);
          		if (!elementsToIterate.isEmpty()) {
          		    // 循环遍历所有待注入的元素
          			for (InjectedElement element : elementsToIterate) {
          				if (logger.isTraceEnabled()) {
          					logger.trace("Processing injected element of bean '" + beanName + "': " + element);
          				}
          				// 调用注入方法
          				element.inject(target, beanName, pvs);
          			}
          		}
          	}
          

          基于这个原因,dubbo定义了ReferenceInjectionMetadata类,代码如下:

           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) {
                      // 构造函数接收3个参数,类型,待注入的属性元素,待注入的方法元素
                      // 把fieldElements和methodElements的内容合并,作为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;
                  }
              }
          

          代码很简单,入参变为3个,属性和方法列表区分开,然后把两者合并起来,调用父类的构造函数,用fieldElements保存属性列表,用methodElements保存方法列表。

          ReferenceFieldElement

          属性注入实际发生在ReferenceFieldElement类,代码如下:

           private class ReferenceFieldElement extends InjectionMetadata.InjectedElement {
          
                  private final Field field;
          
                  private final Reference reference;
          
                  private volatile ReferenceBean<?> referenceBean;
          
                  // 构造函数会传入要设置的Field对象,Reference注解对象
                  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();
          			// 构建ReferenceBean对象
                      referenceBean = buildReferenceBean(reference, referenceClass);
          			// 将属性设置为可访问的
                      ReflectionUtils.makeAccessible(field);
          			// 给Field对象设置属性
                      field.set(bean, referenceBean.getObject());
          
                  }
          
              }
          

          ReferenceMethodElement

          方法注入实际发生在ReferenceMethodElement类,代码如下:

          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 {
          			// 获取类型
                      Class<?> referenceClass = pd.getPropertyType();
          			// 构建ReferenceBean对象
                      referenceBean = buildReferenceBean(reference, referenceClass);
          			// 将方法设置为可访问
                      ReflectionUtils.makeAccessible(method);
          			// 把referenceBean生成的对象作为入参,调用bean对象的method方法
          			// 看到这里,就能明白,@Reference也可以加在那些setXXX方法上
          			// 例如有个userService属性,有个setUserService方法,可以在setUserService方法上加@Reference注解
                      method.invoke(bean, referenceBean.getObject());
          
                  }
          
              }
          

          为什么加了@Reference注解的属性是null

          从上面的代码分析,可以知道属性的注入,是靠ReferenceAnnotationBeanPostProcessor后置处理来触发,往filed设置值。

          如果这一过程中,发生异常,导致没有成功为field设置值,则加了@Referencce的属性就会一直是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:1python31)

              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(DefaultListableBeahttp://www.devze.comnFactory.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)

          &n编程客栈bsp;   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.

          看这一段错误日志,当SearcherService没有任何provider启动的时候调用ReferenceBean.getObject方法,就会抛IllegalStateException异常,设置属性失败。

          网上很多说,遇到加@Reference注解的属性为null的,应该就是这个情况。

          什么情况会抛No provider的IllegalStateException异常

          ReferenceAnnotationBeanPostProcessor.buildReferenceBean

             private ReferenceBean<?> buildReferenceBean(Reference reference, Class<?> referenceClass) throws Exception {
          
                  String referenceBeanCacheKey = generateReferenchttp://www.devze.comeBeanCacheKey(reference, referenceClass);
          
                  ReferenceBean<?> referenceBean = referenceBeansCache.get(referenceBeanCacheKey);
          
                  if (referenceBean == null) {
          
                      ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
                              .create(reference, classLoader, applicationContext)
                              .interfaceClass(referenceClass);
          			// 这里报错
                      referenceBean = beanBuilder.build();
          
                      referenceBeansCache.putIfAbsent(referenceBeanCacheKey, referenceBean);
          
                  }
          
                  return referenceBean;
          
              }
          

          buildReferenceBean方法调用ReferenceBeanBuilder.build报错

          ReferenceBeanBuilder.build

          ReferenceBeanBuilder.build方法是它的父类AbstractAnnotationConfigBeanBuilder的

           public final B build() throws Exception {
          
                  checkDependencies();
          		
                  B bean = doBuild();
          
                  configureBean(bean);
                  
                  if (logger.isInfoEnabled()) {
                  	// 这里报错
                      logger.info(bean + " has been built.");
                  }
          
                  return bean;
          
              }
          

          ReferenceBeanBuilder.doBuild

          ReferenceBeanBuilder重写了doBuild方法,返回ReferenceBean对象

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

          所以,问题是出在了ReferenceBean.toString方法上

          AbstractConfig.toString

          ReferenceBean并没有重写toString方法,但他的根父类是AbstractConfig,看错误日志开发者_JAVA教程,可以看到这个:

          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();
                      // 拿到当前类的所有方法
                      for (Method method : methods) {
                          try {
                              String name = method.getName();
                              // 过滤剩下get和is开头的方法,但不包括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);
                                  // 反射获取方法返回值,拼接字符串
                                  // 就是这里报空指针
                                  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类实现类FactoryBean接口,实现了getObject方法,getObject方法满足get开头的条件,会被AbstractConfig.toString方法调用到

          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);
              }
          
          

          省略了大部分代码,只保留了比较重要的,调用getObject方法,会判断是否初始化过,如果初始化过,直接返回ref;如果没有初始化,则会进行初始化,然后调用createProxy方法来创建代理,如果我们没有配置consumer的check或者check=true,则会检查invoker对象的可用性“invoker.isAvailable()”,如果不可用,就会抛IllegalStateException异常。

          避免@Reference注解的属性为null

          配置消费者的检查为false,即@Reference(check=false)

          ReferenceBean.getObject调用时机的猜测

          看的ReferenceFieldElement.inject方法,很容易以为IllegalStateException是在 “field.set(bean, referenceBean.getObject());”这一行报错的,但实际上是在 “referenceBean = buildReferenceBean(reference, referenceClass);”

           @Override
                  protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
          
                      Class<?> referenceClass = field.getType();
          			// 构建ReferenceBean对象
                      referenceBean = buildReferenceBean(reference, referenceClass);
          			// 将属性设置为可访问的
                      ReflectionUtils.makeAccessible(field);
          			// 给Field对象设置属性
                      field.set(bean, referenceBean.getObject());
          
                  }
          

          为什么要在AbstractConfig.toString就调用了getObject方法,触发报错呢?

          如果AbstractConfig.toString过滤掉getObject方法,会发生什么事情呢?

          InjectionMetadata.inject方法是遍历checkedElements列表,挨个调用element.inject方法。

          如果AbstractConfig.toString过滤掉getObject方法,则首次调用ReferenceBean.getObject方法是在“field.set(bean, referenceBean.getObject());”。异常没有被catch住,checkedElements列表的遍历会被打断。

          会出现这样的情况,有一个bean需要注入5个代理对象,但是调用第一个ReferenceBean.getObject的时候抛异常,则注入行为被中断,另外4个属性也没有被注入。

          总结

          以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

          0

          精彩评论

          暂无评论...
          验证码 换一张
          取 消

          关注公众号