Abbiamo lo stesso caso d'uso. Come già accennato, uno dei problemi principali con la ricreazione di un bean durante il runtime è come aggiornare i riferimenti che sono già stati iniettati. Questo presenta la sfida principale.
Per risolvere questo problema, ho utilizzato la classe Java AtomicReference <>. Invece di iniettare direttamente il bean, l'ho avvolto come AtomicReference e poi lo inietto. Poiché l'oggetto racchiuso da AtomicReference può essere ripristinato in modo thread-safe, sono in grado di utilizzarlo per modificare l'oggetto sottostante quando viene rilevata una modifica del database. Di seguito è riportato un esempio di configurazione/utilizzo di questo modello:
@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;
}
}
I quindi iniettare il bean dove necessario, ad es.
In una classe separata, eseguo un thread dello scheduler che viene avviato all'avvio del contesto dell'applicazione. La classe di simile a questa:
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 {
}
}
}
io sono ancora sul recinto, se questo è qualcosa che vorrei sostenere farlo in quanto ha un leggero odore. Ma in un uso limitato e attento fornisce un approccio alternativo al caso d'uso dichiarato. Si prega di notare che da un punto di vista di Kafka questo esempio di codice lascerà aperto il vecchio produttore. In realtà bisognerebbe effettuare correttamente una chiamata a filo() sul vecchio produttore per chiuderla. Ma non è quello che l'esempio intende dimostrare.
le proprietà dei bean vengono caricate nella fase di post processing di Bean Factory e non penso che saremo in grado di fare molto dopo di ciò in termini di ricaricamento specialmente nei singleton, dovresti ricostruire il contesto dell'app in cui sono caricati i bean, ecco un esempio interessante, http://stackoverflow.com/questions/4084890/spring-replacing-the-bean-property-values-with-new-property-file-values – mariubog