<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have searched the <a href="https://github.com/apache/dubbo/issues">issues</a> of this repository and believe that this is not a duplicate.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/apache/dubbo/blob/master/FAQ.md">FAQ</a> of this repository and believe that this is not a duplicate.</li>
</ul>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Dubbo version: 2.6.5/2.7.4.x</li>
<li>Operating System version: OSX 10.x</li>
<li>Java version: 1.8</li>
</ul>
<p dir="auto">在配置 dubbo.provider.token 后，有时 dubbo url 中 default.token 的值会变成 false，而非配置的 token。原因是 getMethods()、getDeclaredMethods() 不保证顺序，导致属性绑定失败。</p>
<p dir="auto">Spring Boot 中，dubbo 会注入多个beanPostProcessor，在 postProcessBeforeInitialization 阶段，利用 dubboConfigBinder 对 dubbo 相关的 XxxConfig bean 进行属性绑定。如下图所示， ProviderConfig 类是一个 bean</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/9402727/72245934-99803a80-362c-11ea-80fe-bf6fba7d190c.png"><img src="https://user-images.githubusercontent.com/9402727/72245934-99803a80-362c-11ea-80fe-bf6fba7d190c.png" alt="image" style="max-width: 100%;"></a></p>
<p dir="auto">dubboConfigBinder 在 boot 项目中，默认的实现是 RelaxedDubboConfigBinder，具体实现方式是使用 Binder 类，此处可看源码。</p>
<p dir="auto">绑定属性过程中，利用 JavaBeanBinder$Bean 缓存相关类属性的 getter、setter 方法。</p>
<div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="private final Map&lt;String, BeanProperty&gt; properties = new LinkedHashMap&lt;&gt;();

Bean(ResolvableType resolvableType, Class&lt;?&gt; type) {
     this.resolvableType = resolvableType;
     this.type = type;
     putProperties(type);
}
private void putProperties(Class&lt;?&gt; type) {
     while (type != null &amp;&amp; !Object.class.equals(type)) {

        // getDeclaredMethods() 返回方法顺序的具有不确定性
	for (Method method : type.getDeclaredMethods()) {
		if (isCandidate(method)) {
		    addMethod(method);
		}
	}
	for (Field field : type.getDeclaredFields()) {
		addField(field);
	}
	type = type.getSuperclass();
     }
}
"><pre class="notranslate"><span class="pl-k">private</span> <span class="pl-k">final</span> <span class="pl-smi">Map</span>&lt;<span class="pl-smi">String</span>, <span class="pl-smi">BeanProperty</span>&gt; <span class="pl-s1">properties</span> = <span class="pl-k">new</span> <span class="pl-smi">LinkedHashMap</span>&lt;&gt;();

<span class="pl-en">Bean</span>(<span class="pl-smi">ResolvableType</span> <span class="pl-s1">resolvableType</span>, <span class="pl-smi">Class</span>&lt;?&gt; <span class="pl-s1">type</span>) {
     <span class="pl-smi">this</span>.<span class="pl-s1">resolvableType</span> = <span class="pl-s1">resolvableType</span>;
     <span class="pl-smi">this</span>.<span class="pl-s1">type</span> = <span class="pl-s1">type</span>;
     <span class="pl-en">putProperties</span>(<span class="pl-s1">type</span>);
}
<span class="pl-k">private</span> <span class="pl-smi">void</span> <span class="pl-s1">putProperties</span>(<span class="pl-smi">Class</span>&lt;?&gt; <span class="pl-s1">type</span>) {
     <span class="pl-k">while</span> (<span class="pl-s1">type</span> != <span class="pl-c1">null</span> &amp;&amp; !<span class="pl-smi">Object</span>.<span class="pl-k">class</span>.<span class="pl-en">equals</span>(<span class="pl-s1">type</span>)) {

        <span class="pl-c">// getDeclaredMethods() 返回方法顺序的具有不确定性</span>
	<span class="pl-k">for</span> (<span class="pl-smi">Method</span> <span class="pl-s1">method</span> : <span class="pl-s1">type</span>.<span class="pl-en">getDeclaredMethods</span>()) {
		<span class="pl-k">if</span> (<span class="pl-en">isCandidate</span>(<span class="pl-s1">method</span>)) {
		    <span class="pl-en">addMethod</span>(<span class="pl-s1">method</span>);
		}
	}
	<span class="pl-k">for</span> (<span class="pl-smi">Field</span> <span class="pl-s1">field</span> : <span class="pl-s1">type</span>.<span class="pl-en">getDeclaredFields</span>()) {
		<span class="pl-en">addField</span>(<span class="pl-s1">field</span>);
	}
	<span class="pl-s1">type</span> = <span class="pl-s1">type</span>.<span class="pl-en">getSuperclass</span>();
     }
}</pre></div>
<p dir="auto">由于 getDeclaredMethods() 返回方法顺序的不确定性，AbstractServiceConfig 存在两个 setToken(boolean) 和 setToken(string)，<br>
因此当 setToken(boolean) 顺序靠前时，因为方法参数类型不满足，就无法绑定。</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/9402727/72246166-1c08fa00-362d-11ea-97cc-60c8786c9a6f.png"><img src="https://user-images.githubusercontent.com/9402727/72246166-1c08fa00-362d-11ea-97cc-60c8786c9a6f.png" alt="image" style="max-width: 100%;"></a></p>
<p dir="auto">以下为 setToken(string) 顺序靠前时，正确的绑定内容</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/9402727/72246135-101d3800-362d-11ea-9834-41ae87c60152.png"><img src="https://user-images.githubusercontent.com/9402727/72246135-101d3800-362d-11ea-9834-41ae87c60152.png" alt="image" style="max-width: 100%;"></a></p>
<p dir="auto">在之后运行到触发 ContextRefreshedEvent 事件后，ServiceBean 执行 export() 方法，其中会填充 provider 的属性。调用栈如下。</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="ServieBean#export()
 |- doExport()
   |- checkDefault()
     |- AbstractConfig.appendProperties(provider)"><pre class="notranslate"><code class="notranslate">ServieBean#export()
 |- doExport()
   |- checkDefault()
     |- AbstractConfig.appendProperties(provider)
</code></pre></div>
<p dir="auto">在 appendProperties 的实现中，getMethods 返回的方法顺序，也是 setToken(boolean) 优先。</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/9402727/72245934-99803a80-362c-11ea-80fe-bf6fba7d190c.png"><img src="https://user-images.githubusercontent.com/9402727/72245934-99803a80-362c-11ea-80fe-bf6fba7d190c.png" alt="image" style="max-width: 100%;"></a></p>
<p dir="auto">通过反射将属性注入 ProviderConfig，在注入之前，会通过 getter 方法判断属性是否已经设置，如果在前一步 Spring 的属性绑定中是正确的，因为 token 已经有值，则不会运行的注入以下代码。</p>
<div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// AbstractConfig.appendProperties(provider)
if (value != null &amp;&amp; value.length() &gt; 0) {
    method.invoke(config, new Object[]{convertPrimitive(method.getParameterTypes()[0], value)});
}"><pre class="notranslate"><span class="pl-c">// AbstractConfig.appendProperties(provider)</span>
<span class="pl-k">if</span> (<span class="pl-s1">value</span> != <span class="pl-c1">null</span> &amp;&amp; <span class="pl-s1">value</span>.<span class="pl-en">length</span>() &gt; <span class="pl-c1">0</span>) {
    <span class="pl-s1">method</span>.<span class="pl-en">invoke</span>(<span class="pl-s1">config</span>, <span class="pl-k">new</span> <span class="pl-smi">Object</span>[]{<span class="pl-en">convertPrimitive</span>(<span class="pl-s1">method</span>.<span class="pl-en">getParameterTypes</span>()[<span class="pl-c1">0</span>], <span class="pl-s1">value</span>)});
}</pre></div>
<p dir="auto">此处注入时，因为此处的方法是 setToken(boolean)。，convertPrimitive 会将 string 类型的 token 转换的目标的 boolean 类型，Boolean.valueOf 方法就会将我们填写的 string 类型转换为 false 了。</p>
<p dir="auto">以上就是 default.token=false 的原因</p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have searched the <a href="https://github.com/apache/dubbo/issues">issues</a> of this repository and believe that this is not a duplicate.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/apache/dubbo/blob/master/FAQ.md">FAQ</a> of this repository and believe that this is not a duplicate.</li>
</ul>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Dubbo version: 2.7.3</li>
<li>Operating System version: macOs sierra</li>
<li>Java version: 1.8</li>
</ul>
<h3 dir="auto">Steps to reproduce this issue</h3>
<ol dir="auto">
<li>start the provider</li>
<li>dynamic config changed through override://ip:port?xx=xxx</li>
<li>service unregistered with provider url changed.</li>
</ol>
<p dir="auto">From: RegistryProtocol.java</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="public &lt;T&gt; void reExport(final Invoker&lt;T&gt; originInvoker, URL newInvokerUrl) {
    ...
    if (providerInvokerWrapper.isReg() &amp;&amp; !registeredProviderUrl.equals(providerInvokerWrapper.getProviderUrl())) {
        unregister(registryUrl, providerInvokerWrapper.getProviderUrl());
        register(registryUrl, registeredProviderUrl);
        newProviderInvokerWrapper.setReg(true);
    }
    ...
}"><pre class="notranslate"><code class="notranslate">public &lt;T&gt; void reExport(final Invoker&lt;T&gt; originInvoker, URL newInvokerUrl) {
    ...
    if (providerInvokerWrapper.isReg() &amp;&amp; !registeredProviderUrl.equals(providerInvokerWrapper.getProviderUrl())) {
        unregister(registryUrl, providerInvokerWrapper.getProviderUrl());
        register(registryUrl, registeredProviderUrl);
        newProviderInvokerWrapper.setReg(true);
    }
    ...
}
</code></pre></div>
<p dir="auto">Pls. provide [GitHub address] to reproduce this issue.</p>
<h3 dir="auto">Expected Result</h3>
<p dir="auto">Different registry have different interface for dynamic config changed. For our registry, unregister will cause service be pulled out. But for some registry, maybe they are relying on the unregister and register. We have to find out the way to compatible with different registry.</p>
<p dir="auto">If there is an exception, please attach the exception trace:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Just put your stack trace here!"><pre class="notranslate"><code class="notranslate">Just put your stack trace here!
</code></pre></div>