目录

现象

看源码分析原因

注解Reference第一步:用Reference注解里的参数初始化ReferenceConfig

注解Reference第二步:从配置文件里获取参数,写入ReferenceConfig

注解Reference第三步:生成Consumer代理

解决方案


如果只想知道怎么解决,请翻到文章最后一句。

现象

当使用Dubbo的Reference注解时,必须先启动provider,再启动consumer,否则被Reference注解的service就是null,且后来启动了provider之后,service也依然是null,不能使用。

被Reference注解的service是null,说明Dubbo的代理没有生成。

在Reference注解中配置check=false,没有用。

在springboot的配置文件application.properties中各种花式配置check=false,没有用。

看源码分析原因

Spring处理Reference注解一共分三步。

注解Reference第一步:用Reference注解里的参数初始化ReferenceConfig

当Spring处理Controller注解时,使用Annotation类作为Controller的处理类,Spring会调用处理类的postProcessBeforeInitialization方法,当发现Controller类里有Reference注解的service时,会调用Annotation的refer方法来初始化被Reference注解的service,代码如下:

private Object refer(Reference reference, Class<?> referenceClass) {String interfaceName;if (!"".equals(reference.interfaceName())) {interfaceName = reference.interfaceName();} else if (!Void.TYPE.equals(reference.interfaceClass())) {interfaceName = reference.interfaceClass().getName();} else {if (!referenceClass.isInterface()) {throw new IllegalStateException("The @Reference undefined interfaceClass or interfaceName, and the property type " + referenceClass.getName() + " is not a interface.");}interfaceName = referenceClass.getName();}String key = reference.group() + "/" + interfaceName + ":" + reference.version();ReferenceBean<?> referenceConfig = (ReferenceBean)this.referenceConfigs.get(key);if (referenceConfig == null) {referenceConfig = new ReferenceBean(reference);if (Void.TYPE.equals(reference.interfaceClass()) && "".equals(reference.interfaceName()) && referenceClass.isInterface()) {referenceConfig.setInterface(referenceClass);}if (this.applicationContext != null) {referenceConfig.setApplicationContext(this.applicationContext);if (reference.registry() != null && reference.registry().length > 0) {List<RegistryConfig> registryConfigs = new ArrayList();String[] arr$ = reference.registry();int len$ = arr$.length;for(int i$ = 0; i$ < len$; ++i$) {String registryId = arr$[i$];if (registryId != null && registryId.length() > 0) {registryConfigs.add((RegistryConfig)this.applicationContext.getBean(registryId, RegistryConfig.class));}}referenceConfig.setRegistries(registryConfigs);}if (reference.consumer() != null && reference.consumer().length() > 0) {referenceConfig.setConsumer((ConsumerConfig)this.applicationContext.getBean(reference.consumer(), ConsumerConfig.class));}if (reference.monitor() != null && reference.monitor().length() > 0) {referenceConfig.setMonitor((MonitorConfig)this.applicationContext.getBean(reference.monitor(), MonitorConfig.class));}if (reference.application() != null && reference.application().length() > 0) {referenceConfig.setApplication((ApplicationConfig)this.applicationContext.getBean(reference.application(), ApplicationConfig.class));}if (reference.module() != null && reference.module().length() > 0) {referenceConfig.setModule((ModuleConfig)this.applicationContext.getBean(reference.module(), ModuleConfig.class));}if (reference.consumer() != null && reference.consumer().length() > 0) {referenceConfig.setConsumer((ConsumerConfig)this.applicationContext.getBean(reference.consumer(), ConsumerConfig.class));}try {referenceConfig.afterPropertiesSet();} catch (RuntimeException var11) {throw var11;} catch (Exception var12) {throw new IllegalStateException(var12.getMessage(), var12);}}this.referenceConfigs.putIfAbsent(key, referenceConfig);referenceConfig = (ReferenceBean)this.referenceConfigs.get(key);}return referenceConfig.get();
}

其中

referenceConfig = new ReferenceBean(reference);

这行表示用Reference注解生成ReferenceConfig代理,注解中配置的参数都会赋值给ReferenceConfig,实际上该方法调用的是ReferenceConfig的父类AbstractConfig类的

appendAnnotation(Class<?> annotationClass, Object annotation)

方法,其中的annotationClass参数就是com.alibaba.dubbo.config.annotation.Reference的class,annotation参数就是本次初始化的Reference对象的代理。

appendAnnotation方法的代码如下:

protected void appendAnnotation(Class<?> annotationClass, Object annotation) {Method[] methods = annotationClass.getMethods();Method[] arr$ = methods;int len$ = methods.length;for(int i$ = 0; i$ < len$; ++i$) {Method method = arr$[i$];if (method.getDeclaringClass() != Object.class && method.getReturnType() != Void.TYPE && method.getParameterTypes().length == 0 && Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) {try {String property = method.getName();if ("interfaceClass".equals(property) || "interfaceName".equals(property)) {property = "interface";}String setter = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);Object value = method.invoke(annotation);if (value != null && !value.equals(method.getDefaultValue())) {Class<?> parameterType = ReflectUtils.getBoxedClass(method.getReturnType());if (!"filter".equals(property) && !"listener".equals(property)) {if ("parameters".equals(property)) {parameterType = Map.class;value = CollectionUtils.toStringMap((String[])((String[])value));}} else {parameterType = String.class;value = StringUtils.join((String[])((String[])value), ",");}try {Method setterMethod = this.getClass().getMethod(setter, parameterType);setterMethod.invoke(this, value);} catch (NoSuchMethodException var13) {;}}} catch (Throwable var14) {logger.error(var14.getMessage(), var14);}}}}

注意其中的

if (value != null && !value.equals(method.getDefaultValue())) {

这一行,意思是如果Reference注解的参数等于默认值,则不会把设置的参数值写入ReferenceConfg。

也就是说,如果我这样使用Reference注解:

@Reference(check = false)
TestService testService;

那么check参数不会被写入ReferenceConfig,生成的ReferenceConfig依然是:

<dubbo:reference singleton="true" generic="false" />

里面没有check参数。

这个规则可能是为了以后在生成Consumer代理的时候少循环几次,但是会引发一些问题。

此时是调用栈大概是这样的:

appendAnnotation:101, AbstractConfig (com.alibaba.dubbo.config)

<init>:122, ReferenceConfig (com.alibaba.dubbo.config)

<init>:56, ReferenceBean (com.alibaba.dubbo.config.spring)

refer:259, AnnotationBean (com.alibaba.dubbo.config.spring)

postProcessBeforeInitialization:233, AnnotationBean (com.alibaba.dubbo.config.spring)

applyBeanPostProcessorsBeforeInitialization:422, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)

initializeBean:1694, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)

doCreateBean:579, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)

createBean:501, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)

lambda$doGetBean$0:317, AbstractBeanFactory (org.springframework.beans.factory.support)

getObject:-1, 530696881 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$97)

getSingleton:228, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)

doGetBean:315, AbstractBeanFactory (org.springframework.beans.factory.support)

getBean:199, AbstractBeanFactory (org.springframework.beans.factory.support)

preInstantiateSingletons:760, DefaultListableBeanFactory (org.springframework.beans.factory.support)

finishBeanFactoryInitialization:869, AbstractApplicationContext (org.springframework.context.support)

refresh:550, AbstractApplicationContext (org.springframework.context.support)

refresh:140, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)

refresh:759, SpringApplication (org.springframework.boot)

refreshContext:395, SpringApplication (org.springframework.boot)

run:327, SpringApplication (org.springframework.boot)

run:1255, SpringApplication (org.springframework.boot)

run:1243, SpringApplication (org.springframework.boot)

main:11,TestApplication

注解Reference第二步:从配置文件里获取参数,写入ReferenceConfig

把注解的参数写入ReferenceConfig参数后,在refer方法最后一行,

return referenceConfig.get();

正式开始初始化ReferenceConfig。

get方法代码:

public synchronized T get() {if (this.destroyed) {throw new IllegalStateException("Already destroyed!");} else {if (this.ref == null) {this.init();}return this.ref;}
} 

init()方法很长,配置了很多ReferenceConfig的参数,其中单独调用了一下appendProperties方法,这个方法负责把配置文件中的属性写入ReferenceConfig,代码如下:

protected static void appendProperties(AbstractConfig config) {if (config != null) {String prefix = "dubbo." + getTagName(config.getClass()) + ".";Method[] methods = config.getClass().getMethods();Method[] arr$ = methods;int len$ = methods.length;for(int i$ = 0; i$ < len$; ++i$) {Method method = arr$[i$];try {String name = method.getName();if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == 1 && isPrimitive(method.getParameterTypes()[0])) {String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");String value = null;String pn;if (config.getId() != null && config.getId().length() > 0) {pn = prefix + config.getId() + "." + property;value = System.getProperty(pn);                //注释一if (!StringUtils.isBlank(value)) {logger.info("Use System Property " + pn + " to config dubbo");}}if (value == null || value.length() == 0) {pn = prefix + property;value = System.getProperty(pn);                //注释二if (!StringUtils.isBlank(value)) {logger.info("Use System Property " + pn + " to config dubbo");}}if (value == null || value.length() == 0) {Method getter;try {getter = config.getClass().getMethod("get" + name.substring(3));} catch (NoSuchMethodException var14) {try {getter = config.getClass().getMethod("is" + name.substring(3));} catch (NoSuchMethodException var13) {getter = null;}}if (getter != null && getter.invoke(config) == null) {if (config.getId() != null && config.getId().length() > 0) {value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);        //注释三}if (value == null || value.length() == 0) {value = ConfigUtils.getProperty(prefix + property);      //注释四}if (value == null || value.length() == 0) {String legacyKey = (String)legacyProperties.get(prefix + property);if (legacyKey != null && legacyKey.length() > 0) {value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));      //注释五}}}}if (value != null && value.length() > 0) {method.invoke(config, convertPrimitive(method.getParameterTypes()[0], value));}}} catch (Exception var15) {logger.error(var15.getMessage(), var15);}}}
}

方法中设置了几个参数,假设我要把check参数赋值给Reference,那这几个参数如下:

prefix:dubbo.reference

property:check

config.getId():注解的service带路径的全名,我测试时用的service是test.TestService

根据这个方法的代码流程,赋值check有以下几次机会:

1,注释一

pn = prefix + config.getId() + "." + property;value = System.getProperty(pn);                //注释一

此时的pn的值是dubbo.reference. test.TestService.check,只要系统参数里有这个配置,比如

-Ddubbo.reference. test.TestService.check=false

就可以向ReferenceConfig赋值。

2,注释二

pn = prefix + property;value = System.getProperty(pn);                //注释二

此时的pn的值是dubbo.reference.check,只要系统参数里有这个配置,比如

-Ddubbo.reference.check=false

就可以向ReferenceConfig赋值。

可见在此处的配置中,单个service配置的优先级高于全局配置的优先级。

3,注释三

value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);   //注释三

用ConfigUtils获取参数,key值为:

dubbo.reference. test.TestService.check

ConfigUtils获取参数的流程是:

①,先检查系统参数也就是System.getProperty(pn),同注释一,此场景中显然没有值,要不也不会来到注释三了。

②,查找配置文件,配置文件的路径是用以下方法定义的:

public static Properties getProperties() {if (PROPERTIES == null) {Class var0 = ConfigUtils.class;synchronized(ConfigUtils.class) {if (PROPERTIES == null) {String path = System.getProperty("dubbo.properties.file");if (path == null || path.length() == 0) {path = System.getenv("dubbo.properties.file");if (path == null || path.length() == 0) {path = "dubbo.properties";}}PROPERTIES = loadProperties(path, false, true);}}}return PROPERTIES;
}

也就是按如下顺序查找配置文件是否存在

System.getProperty("dubbo.properties.file");

System.getenv("dubbo.properties.file");

dubbo.properties

前两个是查看是否存在dubbo.properties.file配置,最后是查看是否存在dubbo.properties文件。

③,从配置文件中获取dubbo.reference. test.TestService.check参数。

4,注释四

value = ConfigUtils.getProperty(prefix + property);      //注释四

ConfigUtils中获取dubbo.reference.check参数。

可见此处的配置中,单个service配置的优先级高于全局配置的优先级。

5,注释五

String legacyKey = (String)legacyProperties.get(prefix + property);
if (legacyKey != null && legacyKey.length() > 0) {value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));      //注释五
}

legacyProperties是AbstractConfig中定义的一个map,而且在该类中用static代码块初始化:

static {legacyProperties.put("dubbo.protocol.name", "dubbo.service.protocol");legacyProperties.put("dubbo.protocol.host", "dubbo.service.server.host");legacyProperties.put("dubbo.protocol.port", "dubbo.service.server.port");legacyProperties.put("dubbo.protocol.threads", "dubbo.service.max.thread.pool.size");legacyProperties.put("dubbo.consumer.timeout", "dubbo.service.invoke.timeout");legacyProperties.put("dubbo.consumer.retries", "dubbo.service.max.retry.providers");legacyProperties.put("dubbo.consumer.check", "dubbo.service.allow.no.provider");legacyProperties.put("dubbo.service.url", "dubbo.service.address");Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {public void run() {if (AbstractConfig.logger.isInfoEnabled()) {AbstractConfig.logger.info("Run shutdown hook now.");}ProtocolConfig.destroyAll();}}, "DubboShutdownHook"));SUFFIXS = new String[]{"Config", "Bean"};
}

legacyProperties是用来设置一些默认值的,处理Reference注解的时候用不上,但是很多默认值的时候会用到,比如设置Consumer的check属性默认值时,此时的prefix + property是dubbo.consumer.check,所以可以在ConfigUtils可用的配置文件中,配置dubbo.service.allow.no.provider参数,作为所有Consumer的默认check属性。

另外,在这里使用legacyProperties时只能令dubbo.service.allow.no.provider和dubbo.service.max.retry.providers两个参数生效,因为注释五的convertLegacyValue方法是这么写的:

private static String convertLegacyValue(String key, String value) {if (value != null && value.length() > 0) {if ("dubbo.service.max.retry.providers".equals(key)) {return String.valueOf(Integer.parseInt(value) - 1);}if ("dubbo.service.allow.no.provider".equals(key)) {return String.valueOf(!Boolean.parseBoolean(value));}}return value;
}

可见,只用了两个参数,不明白这么设计的初衷是什么,可能别的参数在其他的方法和代码逻辑中会用到。

以上五处注释,就是Dubbo读取配置文件并且装配ReferenceConfig的节点。

注解Reference第三步:生成Consumer代理

参数配置完之后,回到init()方法的最后一行,

this.ref = this.createProxy(map);

map就是之前装配的所有参数的集合,createProxy方法的作用就是生成代理,代码如下:

private T createProxy(Map<String, String> map) {URL tmpUrl = new URL("temp", "localhost", 0, map);boolean isJvmRefer;if (this.isInjvm() == null) {if (this.url != null && this.url.length() > 0) {isJvmRefer = false;} else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {isJvmRefer = true;} else {isJvmRefer = false;}} else {isJvmRefer = this.isInjvm();}if (isJvmRefer) {URL url = (new URL("injvm", "127.0.0.1", 0, this.interfaceClass.getName())).addParameters(map);this.invoker = refprotocol.refer(this.interfaceClass, url);if (logger.isInfoEnabled()) {logger.info("Using injvm service " + this.interfaceClass.getName());}} else {URL u;URL url;if (this.url != null && this.url.length() > 0) {String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(this.url);if (us != null && us.length > 0) {String[] arr$ = us;int len$ = us.length;for(int i$ = 0; i$ < len$; ++i$) {String u = arr$[i$];URL url = URL.valueOf(u);if (url.getPath() == null || url.getPath().length() == 0) {url = url.setPath(this.interfaceName);}if ("registry".equals(url.getProtocol())) {this.urls.add(url.addParameterAndEncoded("refer", StringUtils.toQueryString(map)));} else {this.urls.add(ClusterUtils.mergeUrl(url, map));}}}} else {List<URL> us = this.loadRegistries(false);if (us != null && us.size() > 0) {for(Iterator i$ = us.iterator(); i$.hasNext(); this.urls.add(u.addParameterAndEncoded("refer", StringUtils.toQueryString(map)))) {u = (URL)i$.next();url = this.loadMonitor(u);if (url != null) {map.put("monitor", URL.encode(url.toFullString()));}}}if (this.urls == null || this.urls.size() == 0) {throw new IllegalStateException("No such any registry to reference " + this.interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");}}if (this.urls.size() == 1) {this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));} else {List<Invoker<?>> invokers = new ArrayList();URL registryURL = null;Iterator i$ = this.urls.iterator();while(i$.hasNext()) {url = (URL)i$.next();invokers.add(refprotocol.refer(this.interfaceClass, url));if ("registry".equals(url.getProtocol())) {registryURL = url;}}if (registryURL != null) {u = registryURL.addParameter("cluster", "available");this.invoker = cluster.join(new StaticDirectory(u, invokers));} else {this.invoker = cluster.join(new StaticDirectory(invokers));}}}Boolean c = this.check;if (c == null && this.consumer != null) {c = this.consumer.isCheck();}if (c == null) {c = true;}if (c && !this.invoker.isAvailable()) {throw new IllegalStateException("Failed to check the status of the service " + this.interfaceName + ". No provider available for the service " + (this.group == null ? "" : this.group + "/") + this.interfaceName + (this.version == null ? "" : ":" + this.version) + " from the url " + this.invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());} else {if (logger.isInfoEnabled()) {logger.info("Refer dubbo service " + this.interfaceClass.getName() + " from url " + this.invoker.getUrl());}return proxyFactory.getProxy(this.invoker);}
}

代码主要分两部分,前面大部分代码判断了要生成的consumer在本地有没有provider,如果有就直接使用本地的provider,否则就进行一系列装配,组装用于远端消费的consumer。如果远端的provider地址只有一个,那么这个consumer就直接指定地址并生成一个invoker,如果provider地址有多个,则生成invoker列表。invoker是用来生成代理的。

然后是第二部分,对provider是否存在的判断,也就是这一部分代码:

Boolean c = this.check;
if (c == null && this.consumer != null) {c = this.consumer.isCheck();
}if (c == null) {c = true;
}if (c && !this.invoker.isAvailable()) {throw new IllegalStateException("Failed to check the status of the service " + this.interfaceName + ". No provider available for the service " + (this.group == null ? "" : this.group + "/") + this.interfaceName + (this.version == null ? "" : ":" + this.version) + " from the url " + this.invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
} else {if (logger.isInfoEnabled()) {logger.info("Refer dubbo service " + this.interfaceClass.getName() + " from url " + this.invoker.getUrl());}return proxyFactory.getProxy(this.invoker);
}

代码获取了之前配置的check属性,如果没有check属性,则默认check为true,需要参与后面的provider是否存在判断,如果没有provider,则抛异常,并且不会生成代理。

那么问题就来了,在之前处理Reference参数的时候,如果设置的参数和默认值相同,则不写入Reference,而且 Reference注解中的check默认值就是false,如前文所说,如果代码里写的是

@Reference(check = false)

那么这个check就不会被写到Reference里,如果配置文件里又没有对此进行配置,那么这里的check就会是null,然后在这里被设置为true,然后不得不参与provider是否是null的判断,此时如果没有启动任何provider,那么代理类就再也生成不了了,即使后面启动了provider也没有用。

汇总check参数相关代码逻辑,如下:

注解写法1:

@Reference(check = true)

生成代理的时候check为true。

注解写法2:

@Reference(check = false)

生成Reference时check被忽略,生成代理之前check被设置为true。

注解写法3:

@Reference()

成代理之前check被设置为true。

结果就是无论注解怎么写,最终的check都是true,都必须先起provider。

解决方案

想要先起consumer,就必须保证consumer代理能生成。

要保证consumer代理能生成,就必须配置check=false。

要配置check=false,不能在Reference注解里配置,而是应该在配置文件中配置。

汇总Spring从配置中获取check参数的方式,有以下几种:

1,System.getProperty("dubbo.reference.{id}.check")

其中{id}是被注解的类的全名。

System.getProperty()是从系统变量中获取参数,来自java启动命令的vm options参数。

人工定义此参数的方法是在启动java的命令中加入:-Dcheck=false参数。

可以在IDEA中的Run-->Edit Configurations-->VM options,添加-Dcheck=false参数。

可以在Eclipse中的Run Configurations—>Arguments-->VM arguments,添加-Dcheck=false参数。

2,System.getProperty("dubbo.reference.check")

系统变量中,配置所有Reference注解通用的配置方式。

3,System.getProperty("dubbo.properties.file")

系统变量中,指定配置文件路径,然后在配置文件中配置dubbo.reference.{id}.check或者dubbo.reference.check。

4,System.getenv("dubbo.properties.file")

环境变量中,指定配置文件路径,然后在配置文件中配置dubbo.reference.{id}.check或者dubbo.reference.check。

System.getenv()得到的变量是操作系统级的。

5,dubbo.properies

在resources目录中添加dubbo.properies配置文件,在此文件中添加dubbo.reference.{id}.check或者dubbo.reference.check配置。

以上就是Reference注解必须先启动provider的解决方案。

我选择添加dubbo.properies文件,并且在里面写上,dubbo.reference.check=false。

Dubbo的Reference注解必须先启动provider的问题相关推荐

  1. dubbo的@Reference注解作用分析

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

  2. Dubbo学习记录(八) -- Spring整合Dubbo中@Reference注解解析原理

    Spring整合Dubbo中@Reference注解解析原理 @Reference: 可以用在属性或者方法, 意味着需要引用某个Dubbo服务, 那么Dubbo整合Spring后, 我很好奇怎么把这个 ...

  3. Spring-Boot 整合Dubbo 解决@Reference 注解为null情况

    首先检查一下你的spring boot版本是多少? 如果是2.X 不用看了,spring boot 2.x 必定会出现这个问题, 改为 1.5.9 或其他1.x版本,目前生产环境建议使用1.x版本. ...

  4. Dubbo之@Reference 和 ReferenceBean

    consumer调用dubbo服务 两种方法: 1.构建一个ReferenceBean,然后使用Spring的@Autowired引用服务 @Bean public ReferenceBean< ...

  5. 由浅入深分布式(5)dubbo提供者用内网地址注册provider以及 spring boot admin client用主机名注册spring boot admin server

    之前遇到过dubbo提供者用内网地址注册provider的问题 当时改了host文件成功了 但是没有想为什么会有这个问题 现在使用spring boot admin 来监控spring boot项目出 ...

  6. Dubbo(十一)dubbo的超时重试配置以及启动检查

    一.dubbo超时重试配置 dubbo重试次数配置使用的是retries 属性.可以配置于服务提供者.消费者的接口方法.接口类.以及全局配置范围上.与超时配置优先级一样遵循就近原则,方法上的配置优先于 ...

  7. @Reference注解说明

    @Reference是dubbo的注解也是注入,注入的是分布式中的远程服务对象. 源码: package org.apache.dubbo.config.annotation;import java. ...

  8. Dubbo的@Reference和@Service说明

    前言 @Reference 用在消费端,表明使用的是服务端的什么服务 1 @RestController2 public class RemoteUserController {3 4 5 6 @Re ...

  9. Dubbo扩展点注解之@Activate

    @Activate称为自动激活扩展点注解,主要使用在有多个扩展点实现.需要同时根据不同条件被激活的场景中,如Filter需要多个同时激活,因为每个Filter实现的是不同的功能. @Activate的 ...

最新文章

  1. iOS安全之RSA加密/生成公钥、秘钥 pem文件
  2. Mybatis的解析模块基础
  3. 关于阿里面试的的一个小题
  4. PHP内核探索:Zend引擎
  5. EEPROM存储器--AT24CXX
  6. 阿波罗数据集怎么下载_从2D images 到3D估计:现有最大规模数据集 ApolloCar3D
  7. tar.gz 文件类型(tar文件的解压和压缩)
  8. Android使用 SO 库时要注意的一些问题
  9. 【Elasticsearch】Elasticsearch 索引 索引模板 生命周期 关系
  10. 查看CDH平台各个组件的版本
  11. 浅谈.NET中泛型的基本原理
  12. Windows10:将cmd命令行添加到右键中的方法
  13. JAVA练习题(正则表达式)
  14. 容量治理-扩容、限流和降级
  15. 正交试验法生成测试用例工具PICT
  16. 面试阿里,看这一篇就够了!
  17. java中替换的快捷键是什么_idea替换快捷键,批量处理对象的操作
  18. 按键精灵的5级开发认证,笔试题参考
  19. HTML5期末大作业:动漫A网站设计——动画漫展学习资料电影模板(6页) 网页设计作业 _ 动漫网页设计作业,网页设计作业 _ 动漫网页设计成品,网页设计作业 _ 动漫网页设计成品模板下载
  20. ElasticKibana 安装部署

热门文章

  1. nginx 代理去掉#_Nginx proxy_pass详解
  2. 怪兽充电一年亏损4个亿,共享经济平台如何借助分账系统打开交易新局面?
  3. RK3288 gpio读取
  4. Maven 多profile及指定编译问题
  5. MAC下绕开百度网盘限速下载的方法,三步操作永久生效
  6. linux 时间 偏差,【转】Linux系统时间偏差的纠正 adjtimex
  7. vue中引入高德地图Loca数据可视化
  8. ip地址、域名、DNS解析服务器
  9. 诺基亚智能手机E60精彩屏幕截图
  10. Android开发介绍(基于Android Studio软件)