Lors de l'arrêt d'une application Spring Cloud utilisant Feign et Eureka, il est possible de rencontrer cette exception :
org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'eurekaAutoServiceRegistration': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
Le message d'erreur complet pointe généralement vers la classe NamedContextFactory et la méthode de fermeture du contexte d'application (doClose).
Analyse de la cause racine :
Ce problème survient pendant le processus de destruction du contexte d'application (ApplicationContext). Lors de cette phase, le conteneur IoC arrête et détruit tous les singletons. La séquence de destruction est cruciale ici.
Le bean eurekaAutoServiceRegistration, qui gère l'enregistrement de l'instance dans Eureka, est détruit en premier. Ensuite, le contexte tente de détruire le bean feignContext, qui est responsable de la gestion des contextes d'application pour chaque client Feign. Lorsque feignContext se détruit, il ferme les sous-contextes qui lui sont associés. Cette fermeture publie un événement ContextClosedEvent.
Le bean eurekaAutoServiceRegistration, bien qu'il soit déjà marqué comme détruit, est encore enregistré comme écouteur pour cet événement. Une tentative de traiter cet événement déclenche alors une nouvelle demande de ce bean au BeanFactory. Comme le conteneur est en cours de destruction, la création de beans est interdite, provoqunat l'exception.
Solution : Imposer l'ordre de dépendance
La solution consiste à déclarer explicitement que le bean feignContext dépend du bean eurekaAutoServiceRegistration. Cela garantit que lors de l'arrêt, feignContext sera détruit avant eurekaAutoServiceRegistration, évitant ainsi le conflit d'écoute d'événements.
On peut y parvenir en utilisant un BeanFactoryPostProcessor pour modifier la définition du bean au démarrage :
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Optional;
@Component
public class DependencyOrderConfigurer implements BeanFactoryPostProcessor {
private static final String FEIGN_CONTEXT_BEAN = "feignContext";
private static final String EUREKA_REGISTRATION_BEAN = "eurekaAutoServiceRegistration";
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory registry) throws BeansException {
Optional<BeanDefinition> feignBeanDef = getBeanDefinitionSafely(registry, FEIGN_CONTEXT_BEAN);
Optional<BeanDefinition> eurekaBeanDef = getBeanDefinitionSafely(registry, EUREKA_REGISTRATION_BEAN);
if (feignBeanDef.isPresent() && eurekaBeanDef.isPresent()) {
feignBeanDef.get().setDependsOn(EUREKA_REGISTRATION_BEAN);
}
}
private Optional<BeanDefinition> getBeanDefinitionSafely(ConfigurableListableBeanFactory registry, String beanName) {
if (registry.containsBeanDefinition(beanName)) {
return Optional.ofNullable(registry.getBeanDefinition(beanName));
}
return Optional.empty();
}
}
Cette approche configurationnelle garantit un cycle de vie propre sans modifier le code applicatif existant.