Reputation: 27
I read this Dynamic Configuration Properties in Spring Boot and Spring Cloud And it said that
If your method execution is the one that triggers the initialization, then it all even happens in the same thread.
This is my code, a TestConfigBean:
package com.my.springdemo.chapter3.test;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
@Data
@RefreshScope
public class TestConfig {
@Value("${name}")
private String name;
@PostConstruct
private void print(){
System.out.println("配置为=" + name);
}
}
A controller:
package com.my.springdemo.chapter3.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@ConditionalOnClass(ContextRefresher.class)
@RestController
public class TestController {
@Autowired
private TestConfig config;
@Autowired
private ContextRefresher contextRefresher;
@RequestMapping("/config")
public String config(){
System.out.println("before config= " + config.getName() + ", hasCode=" + config.hashCode());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
contextRefresher.refresh();
System.out.println("After config= " + config.getName() + ", hasCode=" + config.hashCode());
return config.getName();
}
}
Then get url: http://localhost:8080/config, and changed config "name" in my "application.properties" during the thread sleep time(which id 10 seconds as the code shows).
It turns out that the config "name" is changed before and after. Why?Does I take @RefreshScope the wrong way?
Upvotes: 1
Views: 3714
Reputation: 5589
That is the expected behavior of @RefreshScope
bean.
Like the document says
Two consecutive method executions on the same bean in the same thread may be applied to different targets if things get really busy.
@RefreshScope
bean doesn't guarantee that your multiple invocations on the same thread will use the same target bean.
In your sample, you call contextRefresher.refresh();
and it will destroy all refresh scoped beans. And your second invocation of config.getName()
will reinitialize your bean again. That's why you got the different name in case that you changed your config between your two invocations.
If your method execution is the one that triggers the initialization, then it all even happens in the same thread.
Above statement just means that bean initialization itself will be executed on the same thread. It doesn't mean that your two invocations will use the same target.
IMHO, using @RefreshScope
could be a little risky for certain cases.
Update on the question in comments
IMHO, @ConfigurationProperties
annotation is also specially treated in Spring Cloud. If EnvironmentChangedEvent
occurs, Spring beans that are annotated with @ConfigurationProperties
will be re-binded with changed values.
The main difference between @ConfigurationProperties
in Spring Cloud and @RefreshScope
bean is atomicity during the process.
Spring Cloud just starts rebinding process for @ConfigurationProperties
annotated beans WITHOUT creating a new instance of bean when EnvironmentChangedEvent
occurs. It means that any invocation into this bean can occur during this process (namely before this process ends). As a result, a user of this bean can see any intermediate state (ex three properties are changed. But two properties values are applied and one property are not yet applied. Any invocation can occur on this state)
In case of using @RefreshScope
annotation, a proxy bean is created and it is injected instead of your actual target bean. And if the bean is refreshed (by an invocation of refresh() API or another way), your actual target bean is removed from the cache. If any next invocation to your bean occurs, your target bean is re-created and initialized again (and this process is synchronized by the lock). As a result, your any invocation to your bean always occurs on stable state of your bean (after finishing initializing beans)
You can annotation your @ConfigurationProperties
bean with @RefreshScope
. Or you can only use @RefreshScope
annotation with your bean with @Value
annotation on its internal fields.
Anyway, neither @ConfigurationProperties
nor @RefreshScope
can guarantee your expected result for multiple invocations even on the same thread. Hope this helps.
Upvotes: 4