[Spring] @RefreshScope 적용 시 반영이 잘 안될 때
ITWeb/개발일반 2020. 6. 16. 14:51일단 spring config server 에서 설정을 변경해서 적용 합니다.
POST , /actuator/refresh 를 실행 합니다.
그럼 기대 하는 건 변경 된 설정 값이 반영이 되어 있어야 한다는 것입니다.
그러나 반영이 안되어 있어서 디버깅 하다 아래와 같이 event 를 잡아서 처리를 해주니 잘되는 것을 확인 했습니다.
@EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh() {
log.debug("triggering ----> {}", watcherThreshold);
}
참고해서 보셔야 하는 코드는 아래 입니다.
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.context.scope.refresh;
import java.io.Serializable;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.cloud.context.scope.GenericScope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
/**
* <p>
* A Scope implementation that allows for beans to be refreshed dynamically at runtime
* (see {@link #refresh(String)} and {@link #refreshAll()}). If a bean is refreshed then
* the next time the bean is accessed (i.e. a method is executed) a new instance is
* created. All lifecycle methods are applied to the bean instances, so any destruction
* callbacks that were registered in the bean factory are called when it is refreshed, and
* then the initialization callbacks are invoked as normal when the new instance is
* created. A new bean instance is created from the original bean definition, so any
* externalized content (property placeholders or expressions in string literals) is
* re-evaluated when it is created.
* </p>
*
* <p>
* Note that all beans in this scope are <em>only</em> initialized when first accessed, so
* the scope forces lazy initialization semantics.
* </p>
*
* <p>
* The scoped proxy approach adopted here has a side benefit that bean instances are
* automatically {@link Serializable}, and can be sent across the wire as long as the
* receiver has an identical application context on the other side. To ensure that the two
* contexts agree that they are identical, they have to have the same serialization ID.
* One will be generated automatically by default from the bean names, so two contexts
* with the same bean names are by default able to exchange beans by name. If you need to
* override the default ID, then provide an explicit {@link #setId(String) id} when the
* Scope is declared.
* </p>
*
* @author Dave Syer
* @since 3.1
*
*/
@ManagedResource
public class RefreshScope extends GenericScope implements ApplicationContextAware,
ApplicationListener<ContextRefreshedEvent>, Ordered {
private ApplicationContext context;
private BeanDefinitionRegistry registry;
private boolean eager = true;
private int order = Ordered.LOWEST_PRECEDENCE - 100;
/**
* Creates a scope instance and gives it the default name: "refresh".
*/
public RefreshScope() {
super.setName("refresh");
}
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
/**
* Flag to determine whether all beans in refresh scope should be instantiated eagerly
* on startup. Default true.
* @param eager The flag to set.
*/
public void setEager(boolean eager) {
this.eager = eager;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
this.registry = registry;
super.postProcessBeanDefinitionRegistry(registry);
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
start(event);
}
public void start(ContextRefreshedEvent event) {
if (event.getApplicationContext() == this.context && this.eager
&& this.registry != null) {
eagerlyInitialize();
}
}
private void eagerlyInitialize() {
for (String name : this.context.getBeanDefinitionNames()) {
BeanDefinition definition = this.registry.getBeanDefinition(name);
if (this.getName().equals(definition.getScope())
&& !definition.isLazyInit()) {
Object bean = this.context.getBean(name);
if (bean != null) {
bean.getClass();
}
}
}
}
@ManagedOperation(description = "Dispose of the current instance of bean name "
+ "provided and force a refresh on next method execution.")
public boolean refresh(String name) {
if (!name.startsWith(SCOPED_TARGET_PREFIX)) {
// User wants to refresh the bean with this name but that isn't the one in the
// cache...
name = SCOPED_TARGET_PREFIX + name;
}
// Ensure lifecycle is finished if bean was disposable
if (super.destroy(name)) {
this.context.publishEvent(new RefreshScopeRefreshedEvent(name));
return true;
}
return false;
}
@ManagedOperation(description = "Dispose of the current instance of all beans "
+ "in this scope and force a refresh on next method execution.")
public void refreshAll() {
super.destroy();
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
}
- https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java
- https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScopeRefreshedEvent.java
refreshAll() 을 호출 하게 되어서 반영이 잘됩니다.
더불어 그냥 프레임워크에서 제공하는 @Scheduled 사용 하십시오.
@Scheduled(fixedDelayString = "${megatoi.watcher.cpu.period}000")
private void runner() {
megatoiCheckerUtil.setWatcherThreshold(watcherThreshold);
megatoiCheckerUtil.setPayload(getCheckQuery());
megatoiCheckerUtil.setResourceType(ResourceType.CPU);
megatoiCheckerUtil.commonRunner();
}