dubbo之@Reference注解有什么作用

其他教程   发布日期:2023年09月24日   浏览次数:389

这篇文章主要介绍“dubbo之@Reference注解有什么作用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“dubbo之@Reference注解有什么作用”文章能帮助大家解决问题。

    目的

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

    ReferenceAnnotationBeanPostProcessor

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

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

    1. public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
    2. implements MergedBeanDefinitionPostProcessor, PriorityOrdered, ApplicationContextAware, BeanClassLoaderAware,
    3. DisposableBean {
    4. // 缓存加了@Referece注解的元数据信息,key是bean的名称或者类名,value是加了@Reference的属性和方法
    5. private final ConcurrentMap<String, ReferenceInjectionMetadata> injectionMetadataCache =
    6. new ConcurrentHashMap<String, ReferenceInjectionMetadata>(256);
    7. // 缓存new过的ReferenceBean,相同的key,只会产生一个ReferenceBean
    8. private final ConcurrentMap<String, ReferenceBean<?>> referenceBeansCache =
    9. new ConcurrentHashMap<String, ReferenceBean<?>>();
    10. // spring设置属性到bean之前调用该方法
    11. @Override
    12. public PropertyValues postProcessPropertyValues(
    13. PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    14. // 根据bean的类型,获取需要注入的元数据信息
    15. InjectionMetadata metadata = findReferenceMetadata(beanName, bean.getClass(), pvs);
    16. try {
    17. // 注入对象
    18. metadata.inject(bean, beanName, pvs);
    19. } catch (BeanCreationException ex) {
    20. throw ex;
    21. } catch (Throwable ex) {
    22. throw new BeanCreationException(beanName, "Injection of @Reference dependencies failed", ex);
    23. }
    24. return pvs;
    25. }
    26. private InjectionMetadata findReferenceMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
    27. // 如果是自定义的消费者,没有beanName,退化成使用类名作为缓存key
    28. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    29. // 双重检查,判断是否需要刷新注入信息
    30. ReferenceInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    31. // 判断是否需要刷新
    32. if (InjectionMetadata.needsRefresh(metadata, clazz)) {
    33. // 第一次判断为需要刷新,则锁住injectionMetadataCache对象
    34. synchronized (this.injectionMetadataCache) {
    35. // 再次判断是否需要刷新
    36. metadata = this.injectionMetadataCache.get(cacheKey);
    37. if (InjectionMetadata.needsRefresh(metadata, clazz)) {
    38. // 需要刷新,而且原来缓存的信息不为空,清除缓存信息
    39. if (metadata != null) {
    40. metadata.clear(pvs);
    41. }
    42. try {
    43. // 生成新的元数据信息
    44. metadata = buildReferenceMetadata(clazz);
    45. // 放入缓存
    46. this.injectionMetadataCache.put(cacheKey, metadata);
    47. } catch (NoClassDefFoundError err) {
    48. throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
    49. "] for reference metadata: could not find class that it depends on", err);
    50. }
    51. }
    52. }
    53. }
    54. return metadata;
    55. }
    56. private ReferenceInjectionMetadata buildReferenceMetadata(final Class<?> beanClass) {
    57. // 查找加了@Reference注解的属性
    58. Collection<ReferenceFieldElement> fieldElements = findFieldReferenceMetadata(beanClass);
    59. // 查找加了@Reference注解的属性
    60. // !!!!@Reference还能加到方法上!!!还真没试过
    61. // 不过这个不关心,只关注属性的
    62. Collection<ReferenceMethodElement> methodElements = findMethodReferenceMetadata(beanClass);
    63. return new ReferenceInjectionMetadata(beanClass, fieldElements, methodElements);
    64. }
    65. private List<ReferenceFieldElement> findFieldReferenceMetadata(final Class<?> beanClass) {
    66. // 保存加了@Reference注解的属性列表
    67. final List<ReferenceFieldElement> elements = new LinkedList<ReferenceFieldElement>();
    68. ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
    69. @Override
    70. public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
    71. // 获取属性上的@Reference注解
    72. Reference reference = getAnnotation(field, Reference.class);
    73. // 如果存在@Reference注解
    74. if (reference != null) {
    75. // 不支持静态属性的注入
    76. if (Modifier.isStatic(field.getModifiers())) {
    77. if (logger.isWarnEnabled()) {
    78. logger.warn("@Reference annotation is not supported on static fields: " + field);
    79. }
    80. return;
    81. }
    82. // 添加到队列里
    83. elements.add(new ReferenceFieldElement(field, reference));
    84. }
    85. }
    86. });
    87. return elements;
    88. }

    ReferenceInjectionMetadata

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

    ReferenceInjectionMetadata继承spring定义的InjectionMetadata类。

    之所以需要自定义ReferenceInjectionMetadata类,是因为dubbo的@Reference注解可以使用在属性和方法上,需要区分开。但是spring定义的InjectionMetadata类,只支持一个injectedElements集合,代码如下:

    1. public class InjectionMetadata {
    2. private static final Log logger = LogFactory.getLog(InjectionMetadata.class);
    3. private final Class<?> targetClass;
    4. private final Collection<InjectedElement> injectedElements;
    5. @Nullable
    6. private volatile Set<InjectedElement> checkedElements;
    7. public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
    8. // 构造函数接收两个参数,类型,注入的元素
    9. this.targetClass = targetClass;
    10. this.injectedElements = elements;
    11. }
    12. ......
    13. ......
    14. ......
    15. public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    16. Collection<InjectedElement> checkedElements = this.checkedElements;
    17. // 优先使用checkedElements来注入,如果checkedElements为空,则直接使用injectedElements(没有调用checkConfigMembers方法,checkedElements会空)
    18. Collection<InjectedElement> elementsToIterate =
    19. (checkedElements != null ? checkedElements : this.injectedElements);
    20. if (!elementsToIterate.isEmpty()) {
    21. // 循环遍历所有待注入的元素
    22. for (InjectedElement element : elementsToIterate) {
    23. if (logger.isTraceEnabled()) {
    24. logger.trace("Processing injected element of bean '" + beanName + "': " + element);
    25. }
    26. // 调用注入方法
    27. element.inject(target, beanName, pvs);
    28. }
    29. }
    30. }

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

    1. private static class ReferenceInjectionMetadata extends InjectionMetadata {
    2. private final Collection<ReferenceFieldElement> fieldElements;
    3. private final Collection<ReferenceMethodElement> methodElements;
    4. public ReferenceInjectionMetadata(Class<?> targetClass, Collection<ReferenceFieldElement> fieldElements,
    5. Collection<ReferenceMethodElement> methodElements) {
    6. // 构造函数接收3个参数,类型,待注入的属性元素,待注入的方法元素
    7. // 把fieldElements和methodElements的内容合并,作为InjectionMetadata的injectedElements
    8. super(targetClass, combine(fieldElements, methodElements));
    9. this.fieldElements = fieldElements;
    10. this.methodElements = methodElements;
    11. }
    12. private static <T> Collection<T> combine(Collection<? extends T>... elements) {
    13. List<T> allElements = new ArrayList<T>();
    14. for (Collection<? extends T> e : elements) {
    15. allElements.addAll(e);
    16. }
    17. return allElements;
    18. }
    19. public Collection<ReferenceFieldElement> getFieldElements() {
    20. return fieldElements;
    21. }
    22. public Collection<ReferenceMethodElement> getMethodElements() {
    23. return methodElements;
    24. }
    25. }

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

    ReferenceFieldElement

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

    1. private class ReferenceFieldElement extends InjectionMetadata.InjectedElement {
    2. private final Field field;
    3. private final Reference reference;
    4. private volatile ReferenceBean<?> referenceBean;
    5. // 构造函数会传入要设置的Field对象,Reference注解对象
    6. protected ReferenceFieldElement(Field field, Reference reference) {
    7. super(field, null);
    8. this.field = field;
    9. this.reference = reference;
    10. }
    11. @Override
    12. protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    13. Class<?> referenceClass = field.getType();
    14. // 构建ReferenceBean对象
    15. referenceBean = buildReferenceBean(reference, referenceClass);
    16. // 将属性设置为可访问的
    17. ReflectionUtils.makeAccessible(field);
    18. // 给Field对象设置属性
    19. field.set(bean, referenceBean.getObject());
    20. }
    21. }

    ReferenceMethodElement

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

    1. private class ReferenceMethodElement extends InjectionMetadata.InjectedElement {
    2. private final Method method;
    3. private final Reference reference;
    4. private volatile ReferenceBean<?> referenceBean;
    5. protected ReferenceMethodElement(Method method, PropertyDescriptor pd, Reference reference) {
    6. super(method, pd);
    7. this.method = method;
    8. this.reference = reference;
    9. }
    10. @Override
    11. protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    12. // 获取类型
    13. Class<?> referenceClass = pd.getPropertyType();
    14. // 构建ReferenceBean对象
    15. referenceBean = buildReferenceBean(reference, referenceClass);
    16. // 将方法设置为可访问
    17. ReflectionUtils.makeAccessible(method);
    18. // 把referenceBean生成的对象作为入参,调用bean对象的method方法
    19. // 看到这里,就能明白,@Reference也可以加在那些setXXX方法上
    20. // 例如有个userService属性,有个setUserService方法,可以在setUserService方法上加@Reference注解
    21. method.invoke(bean, referenceBean.getObject());
    22. }
    23. }

    为什么加了@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: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.

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

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

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

    ReferenceAnnotationBeanPostProcessor.buildReferenceBean

    1. private ReferenceBean<?> buildReferenceBean(Reference reference, Class<?> referenceClass) throws Exception {
    2. String referenceBeanCacheKey = generateReferenceBeanCacheKey(reference, referenceClass);
    3. ReferenceBean<?> referenceBean = referenceBeansCache.get(referenceBeanCacheKey);
    4. if (referenceBean == null) {
    5. ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
    6. .create(reference, classLoader, applicationContext)
    7. .interfaceClass(referenceClass);
    8. // 这里报错
    9. referenceBean = beanBuilder.build();
    10. referenceBeansCache.putIfAbsent(referenceBeanCacheKey, referenceBean);
    11. }
    12. return referenceBean;
    13. }

    buildReferenceBean方法调用ReferenceBeanBuilder.build报错

    ReferenceBeanBuilder.build

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

    1. public final B build() throws Exception {
    2. checkDependencies();
    3. B bean = doBuild();
    4. configureBean(bean);
    5. if (logger.isInfoEnabled()) {
    6. // 这里报错
    7. logger.info(bean + " has been built.");
    8. }
    9. return bean;
    10. }

    ReferenceBeanBuilder.doBuild

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

    1. @Override
    2. protected ReferenceBean doBuild() {
    3. return new ReferenceBean<Object>();
    4. }

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

    AbstractConfig.toString

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

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

    AbstractConfig.toString代码如下:

    1. @Override
    2. public String toString() {
    3. try {
    4. StringBuilder buf = new StringBuilder();
    5. buf.append("<dubbo:");
    6. buf.append(getTagName(getClass()));
    7. Method[] methods = getClass().getMethods();
    8. // 拿到当前类的所有方法
    9. for (Method method : methods) {
    10. try {
    11. String name = method.getName();
    12. // 过滤剩下get和is开头的方法,但不包括getClass、get和is
    13. if ((name.startsWith("get") || name.startsWith("is"))
    14. && !"getClass".equals(name) && !"get".equals(name) && !"is".equals(name)
    15. && Modifier.isPublic(method.getModifiers())
    16. && method.getParameterTypes().length == 0
    17. && isPrimitive(method.getReturnType())) {
    18. int i = name.startsWith("get") ? 3 : 2;
    19. String key = name.substring(i, i + 1).toLowerCase() + name.substring(i + 1);
    20. // 反射获取方法返回值,拼接字符串
    21. // 就是这里报空指针
    22. Object value = method.invoke(this, new Object[0]);
    23. if (value != null) {
    24. buf.append(" ");
    25. buf.append(key);
    26. buf.append("="");
    27. buf.append(value);
    28. buf.append(""");
    29. }
    30. }
    31. } catch (Exception e) {
    32. logger.warn(e.getMessage(), e);
    33. }
    34. }
    35. buf.append(" />");
    36. return buf.toString();
    37. } catch (Throwable t) {
    38. logger.warn(t.getMessage(), t);
    39. return super.toString();
    40. }
    41. }

    ReferenceBean.getObjet

    ReferenceBean类实现类FactoryBean接口,实现了getObject方法,getObject方法满足get开头的条件,会被AbstractConfig.toString方法调用到

    1. public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
    2. @Override
    3. public Object getObject() throws Exception {
    4. return get();
    5. }
    6. public synchronized T get() {
    7. if (destroyed) {
    8. throw new IllegalStateException("Already destroyed!");
    9. }
    10. if (ref == null) {
    11. init();
    12. }
    13. return ref;
    14. }
    15. private void init() {
    16. if (initialized) {
    17. return;
    18. }
    19. initialized = true;
    20. ......
    21. ......
    22. ref = createProxy(map);
    23. ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
    24. ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
    25. }
    26. private T createProxy(Map<String, String> map) {
    27. ......
    28. ......
    29. Boolean c = check;
    30. if (c == null && consumer != null) {
    31. c = consumer.isCheck();
    32. }
    33. if (c == null) {
    34. c = true; // default true
    35. }
    36. if (c && !invoker.isAvailable()) {
    37. 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());
    38. }
    39. if (logger.isInfoEnabled()) {
    40. logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
    41. }
    42. // create service proxy
    43. return (T) proxyFactory.getProxy(invoker);
    44. }

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

    避免@Reference注解的属性为null

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

    Referenc

    以上就是dubbo之@Reference注解有什么作用的详细内容,更多关于dubbo之@Reference注解有什么作用的资料请关注九品源码其它相关文章!