EnvironmentChangeEvent的Listener执行顺序
一、问题背景
项目中使用Nacos作为配置中心,实现配置动态更新,在配置类中监听EnvironmentChangeEvent
事件对更新后的配置做一些初始化处理,代码如下:
CallbackConfig
@Order()
@Data
@Accessors(chain = true)
@ConfigurationProperties(prefix = "transparent-delivery.callback")
@Component
@RefreshScope
public class CallbackConfig implements InitializingBean, ApplicationListener<EnvironmentChangeEvent> {
private static final Logger log = LoggerFactory.getLogger("CONFIG_LOG");
private List<ChatbotConfig> configs;
private Map<Integer, ChatbotConfig> CHATBOT_MAP;
public ChatbotConfig fetchByUserId(Integer userId) {
return CHATBOT_MAP.get(userId);
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("Config chatbot callback information:{}!", JSON.toJSONString(configs));
initConfigs(configs);
}
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
log.info("Nacos refresh config information the current information is:{}!", JSON.toJSONString(configs));
initConfigs(configs);
}
}
但是每次在Nacos更新配置后,日志打印的值仍是修改之前的,为什么会出现这个现象?
二、EnvironmentChangeEvent的Listener执行顺序
在Nacos上更新配置后,Spring会发布一个RefreshEvent
事件,这个事件最后会被委托给ContextRefresh
去处理,接下来我们结合源码,看一下处理流程。
RefreshEventListener
package org.springframework.cloud.endpoint.event;
public class RefreshEventListener implements SmartApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationReadyEvent) {
handle((ApplicationReadyEvent) event);
}
else if (event instanceof RefreshEvent) {
handle((RefreshEvent) event);
}
}
public void handle(ApplicationReadyEvent event) {
this.ready.compareAndSet(false, true);
}
public void handle(RefreshEvent event) {
if (this.ready.get()) { // don't handle events before app is ready
Set<String> keys = this.refresh.refresh();
}
}
}
当SpringCloud接收到一个
RefreshEvent
事件后会委托给ContextRefresher
的refresh()
方法去处理。
ContextRefresher
package org.springframework.cloud.context.refresh;
/**
* @author Dave Syer
* @author Venil Noronha
*/
public class ContextRefresher {
public synchronized Set<String> refresh() {
Set<String> keys = refreshEnvironment();
this.scope.refreshAll();
return keys;
}
public synchronized Set<String> refreshEnvironment() {
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
return keys;
}
}
ContextRefresher
的Refresh()
方法先将配置文件添加到Environment
中,然后发布EnvironmentChangeEvent
事件。
AbstractApplicationContext
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
// 此处略去部分代码......
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 核心处理逻辑
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
通过事件广播器将事件广播出去。
SimpleApplicationEventMulticaster
package org.springframework.context.event;
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
}
multicastEvent
方法中获取当前事件的所有Listener后,循环调用Listener的onApplicationEvent
方法。
AbstractApplicationEventMulticaster
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
// 此处略去部分代码......
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// Fully synchronized building and caching of a ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.retrievalMutex) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
// Add programmatically registered listeners, including ones coming
// from ApplicationListenerDetector (singleton beans and inner beans).
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
AnnotationAwareOrderComparator.sort(allListeners);
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
retriever.applicationListeners.clear();
retriever.applicationListeners.addAll(allListeners);
}
return allListeners;
}
上述代码中根据事件类型获取所有Listener,然后根据
AnnotationAwareOrderComparator.sort(allListeners)
对Listener进行排序。
三、AnnotationAwareOrderComparator排序原理
AnnotationAwareOrderComparator
public class AnnotationAwareOrderComparator extends OrderComparator {
/**
* This implementation checks for {@link Order @Order} or
* {@link javax.annotation.Priority @Priority} on various kinds of
* elements, in addition to the {@link org.springframework.core.Ordered}
* check in the superclass.
*/
@Override
@Nullable
protected Integer findOrder(Object obj) {
Integer order = super.findOrder(obj);
if (order != null) {
return order;
}
return findOrderFromAnnotation(obj);
}
@Nullable
private Integer findOrderFromAnnotation(Object obj) {
AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
if (order == null && obj instanceof DecoratingProxy) {
return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
}
return order;
}
}
OrderComparator
public class OrderComparator implements Comparator<Object> {
@Override
public int compare(@Nullable Object o1, @Nullable Object o2) {
return doCompare(o1, o2, null);
}
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
boolean p1 = (o1 instanceof PriorityOrdered);
boolean p2 = (o2 instanceof PriorityOrdered);
if (p1 && !p2) {
return -1;
}
else if (p2 && !p1) {
return 1;
}
int i1 = getOrder(o1, sourceProvider);
int i2 = getOrder(o2, sourceProvider);
return Integer.compare(i1, i2);
}
private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) {
Integer order = null;
// 此处略去部分代码
return (order != null ? order : getOrder(obj));
}
protected int getOrder(@Nullable Object obj) {
if (obj != null) {
Integer order = findOrder(obj);
if (order != null) {
return order;
}
}
return Ordered.LOWEST_PRECEDENCE;
}
}
AnnotationAwareOrderComparator
继承OrderComparator
,获取对象的Order值并进行排序,如果Order为空,则会被设置为最小优先级。
OrderUtils获取对象Order值
public abstract class OrderUtils {
/**
* Return the order from the specified annotations collection.
* <p>Takes care of {@link Order @Order} and
* {@code @javax.annotation.Priority}.
* @param element the source element
* @param annotations the annotation to consider
* @return the order value, or {@code null} if none can be found
*/
@Nullable
static Integer getOrderFromAnnotations(AnnotatedElement element, MergedAnnotations annotations) {
if (!(element instanceof Class)) {
return findOrder(annotations);
}
Object cached = orderCache.get(element);
if (cached != null) {
return (cached instanceof Integer ? (Integer) cached : null);
}
Integer result = findOrder(annotations);
orderCache.put(element, result != null ? result : NOT_ANNOTATED);
return result;
}
@Nullable
private static Integer findOrder(MergedAnnotations annotations) {
MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class);
if (orderAnnotation.isPresent()) {
return orderAnnotation.getInt(MergedAnnotation.VALUE);
}
MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION);
if (priorityAnnotation.isPresent()) {
return priorityAnnotation.getInt(MergedAnnotation.VALUE);
}
return null;
}
}
从上面源码可以看出,如果没有设置
@Order
或是javax.annotation.Priority
, Order值返回为null
。
四、EnvironmentChangeEvent的Listener有哪些
EnvironmentChangeEvent的Listener:
- 默认的EnvironmentChangeEvent的Listener
ConfigurationPropertiesRebinder
- 自定义的EnvironmentChangeEvent的Listener
CallbackConfig
其它EnvironmentChangeEvent的Listener暂不做分析处理。
ConfigurationPropertiesRebinder
package org.springframework.cloud.context.properties;
@Component
@ManagedResource
public class ConfigurationPropertiesRebinder
implements ApplicationContextAware, ApplicationListener<EnvironmentChangeEvent> {
@ManagedOperation
public void rebind() {
this.errors.clear();
for (String name : this.beans.getBeanNames()) {
rebind(name);
}
}
@ManagedOperation
public boolean rebind(String name) {
if (!this.beans.getBeanNames().contains(name)) {
return false;
}
if (this.applicationContext != null) {
try {
Object bean = this.applicationContext.getBean(name);
if (AopUtils.isAopProxy(bean)) {
bean = ProxyUtils.getTargetObject(bean);
}
if (bean != null) {
// 此处省略部分代码......
this.applicationContext.getAutowireCapableBeanFactory()
.destroyBean(bean);
this.applicationContext.getAutowireCapableBeanFactory()
.initializeBean(bean, name);
return true;
}
}
// 此处省略异常处理代码......
}
return false;
}
}
在上述Listener中对
@ConfigurationProperties
标注的类进行销毁和重新创建,实现了配置更新。
CallbackConfig
@Order()
@Data
@Accessors(chain = true)
@ConfigurationProperties(prefix = "config.callback")
@Component
@RefreshScope
public class CallbackConfig implements InitializingBean, ApplicationListener<EnvironmentChangeEvent> {
private static final Logger log = LoggerFactory.getLogger("CONFIG_LOG");
private List<ChatbotConfig> configs;
private Map<Integer, ChatbotConfig> CHATBOT_MAP;
public ChatbotConfig fetchByUserId(Integer userId) {
return CHATBOT_MAP.get(userId);
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("Config chatbot callback information:{}!", JSON.toJSONString(configs));
initConfigs(configs);
}
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
log.info("Nacos refresh config information the current information is:{}!", JSON.toJSONString(configs));
initConfigs(configs);
}
}
五、原因分析
EnvironmentChangeEvent的Listener中有个默认的 ConfigurationPropertiesRebinder
,这个Listener实现了配置的动态更新,但是这个Listener未使用@Order
注解,所以优先级是最小的。
我们自定义的EnvironmentChangeEvent的Listener CallbackConfig
中虽然设置了 @Order
注解并且默认值为最小优先级,但是和 ConfigurationPropertiesRebinder
的优先级是相同的,它们的执行顺序取决于加入集合的顺序,所以执行顺序不可控。无法通过设置 @Order
将自定的Listener放在 ConfigurationPropertiesRebinder
之后执行。
六、解决方案
如果是单独使用 @ConfigurationProperties
注解,则可以将配置的初始化验证放在 @PostConstruct
中处理。
There is one exception. Any @ConfigurationProperties bean that is also in @RefreshScope is not rebound when the event is consumed. They could be rebound, but in the light of what happens in @RefreshScope, it would be redundant. Instead, they follow the usual path of @RefreshScope beans.
参考文章
Dynamic Configuration Properties in Spring Boot and Spring Cloud