동일한 유스 케이스가 있습니다. 이미 언급했듯이 런타임 중에 빈을 다시 만드는 주요한 문제 중 하나는 이미 삽입 된 참조를 업데이트하는 방법입니다. 이것은 주요 도전 과제를 제시합니다.
이 문제를 해결하기 위해 Java의 AtomicReference <> 클래스를 사용했습니다. 콩을 직접 주입하는 대신 AtomicReference로 포장 한 다음 주입했습니다. AtomicReference에 의해 래핑 된 객체는 스레드 안전 방식으로 재설정 될 수 있으므로 데이터베이스 변경이 감지 될 때 기본 객체를 변경하는 데이 객체를 사용할 수 있습니다. 다음은이 패턴의 설정/사용 예입니다.
@Configuration
public class KafkaConfiguration {
private static final String KAFKA_SERVER_LIST = "kafka.server.list";
private static AtomicReference<String> serverList;
@Resource
MyService myService;
@PostConstruct
public void init() {
serverList = new AtomicReference<>(myService.getPropertyValue(KAFKA_SERVER_LIST));
}
// Just a helper method to check if the value for the server list has changed
// Not a big fan of the static usage but needed a way to compare the old/new values
public static boolean isRefreshNeeded() {
MyService service = Registry.getApplicationContext().getBean("myService", MyService.class);
String newServerList = service.getPropertyValue(KAFKA_SERVER_LIST);
// Arguably serverList does not need to be Atomic for this usage as this is executed
// on a single thread
if (!StringUtils.equals(serverList.get(), newServerList)) {
serverList.set(newServerList);
return true;
}
return false;
}
public ProducerFactory<String, String> kafkaProducerFactory() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.CLIENT_ID_CONFIG, "...");
// Here we are pulling the value for the serverList that has been set
// see the init() and isRefreshNeeded() methods above
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, serverList.get());
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(configProps);
}
@Bean
@Lazy
public AtomicReference<KafkaTemplate<String, String>> kafkaTemplate() {
KafkaTemplate<String, String> template = new KafkaTemplate<>(kafkaProducerFactory());
AtomicReference<KafkaTemplate<String, String>> ref = new AtomicReference<>(template);
return ref;
}
}
그런 다음 필요에 따라 빈을 주입합니다.
public MyClass1 {
@Resource
AtomicReference<KafkaTemplate<String, String>> kafkaTemplate;
...
}
public MyClass2 {
@Resource
AtomicReference<KafkaTemplate<String, String>> kafkaTemplate;
...
}
별도의 클래스에서 응용 프로그램 컨텍스트가 시작될 때 시작되는 스케줄러 스레드를 실행합니다.
class Manager implements Runnable {
private ScheduledExecutorService scheduler;
public void start() {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(this, 0, 120, TimeUnit.SECONDS);
}
public void stop() {
scheduler.shutdownNow();
}
@Override
public void run() {
try {
if (KafkaConfiguration.isRefreshNeeded()) {
AtomicReference<KafkaTemplate<String, String>> kafkaTemplate =
(AtomicReference<KafkaTemplate<String, String>>) Registry.getApplicationContext().getBean("kafkaTemplate");
// Get new instance here. This will have the new value for the server list
// that was "refreshed"
KafkaConfiguration config = new KafkaConfiguration();
// The set here replaces the wrapped objet in a thread safe manner with the new bean
// and thus all injected instances now use the newly created object
kafkaTemplate.set(config.kafkaTemplate().get());
}
} catch (Exception e){
} finally {
}
}
}
이 그것으로 약간의 냄새가 않기 때문에 내가하는 일을 옹호 할 일이 있다면 내가 울타리에 여전히 이니 클래스는 다음과 같이 보인다. 그러나 제한적이고주의 깊은 사용에서 명시된 유스 케이스에 대한 대체 접근법을 제공합니다. Kafka 관점에서 볼 때이 코드 예제는 이전 제작자를 열어 둡니다. 실제로는 그것을 닫으려면 오래된 제작자에게 flush() 호출을 적절하게 수행해야합니다. 그러나 이것이 그 예가 보여주는 의미는 아닙니다.
출처
2017-11-30 17:45:03
Dan
beans 속성은 Bean Factory 사후 처리 단계에서로드됩니다. 특히 싱글 톤에서 다시로드하는 측면에서 그다지 많은 작업을 수행 할 수 없다고 생각하면 콩을로드 할 응용 프로그램 컨텍스트를 다시 작성해야합니다. 흥미로운 예가 있습니다. http://stackoverflow.com/questions/4084890/spring-replacing-the-bean-property-values-with-new-property-file-values – mariubog