BeanDefinitionOverrideException in Spring Boot

1. Introduzione

L'aggiornamento Spring Boot 2.1 ha sorpreso diverse persone con occorrenze impreviste di BeanDefinitionOverrideException . Può confondere alcuni sviluppatori e indurli a chiedersi cosa sia successo al comportamento di override dei bean in primavera.

In questo tutorial, sveleremo questo problema e vedremo come affrontarlo al meglio.

2. Dipendenze di Maven

Per il nostro progetto Maven di esempio, dobbiamo aggiungere la dipendenza Spring Boot Starter:

 org.springframework.boot spring-boot-starter 2.3.3.RELEASE 

3. Bean Overriding

I bean Spring vengono identificati dai loro nomi all'interno di un ApplicationContext .

Pertanto, l' override del bean è un comportamento predefinito che si verifica quando si definisce un bean all'interno di un ApplicationContext che ha lo stesso nome di un altro bean . Funziona semplicemente sostituendo il precedente bean in caso di conflitto di nomi.

A partire dalla primavera 5.1, è stata introdotta l'eccezione BeanDefinitionOverrideException per consentire agli sviluppatori di lanciare automaticamente l'eccezione per evitare qualsiasi override imprevisto del bean . Per impostazione predefinita, è ancora disponibile il comportamento originale che consente l'override dei bean.

4. Modifica della configurazione per Spring Boot 2.1

Spring Boot 2.1 ha disabilitato l'override dei bean per impostazione predefinita come approccio difensivo. Lo scopo principale è notare in anticipo i nomi dei bean duplicati per evitare di sovrascrivere i bean accidentalmente .

Pertanto, se la nostra applicazione Spring Boot si basa sull'override dei bean, è molto probabile che incontri l' eccezione BeanDefinitionOverrideException dopo aver aggiornato la versione Spring Boot alla 2.1 e successive.

Nelle sezioni successive, esamineremo un esempio in cui si verificherebbe BeanDefinitionOverrideException e quindi discuteremo alcune soluzioni.

5. Identificazione dei fagioli in conflitto

Creiamo due diverse configurazioni Spring, ciascuna con un metodo testBean () , per produrre l' eccezione BeanDefinitionOverrideException:

@Configuration public class TestConfiguration1 { class TestBean1 { private String name; // standard getters and setters } @Bean public TestBean1 testBean(){ return new TestBean1(); } } 
@Configuration public class TestConfiguration2 { class TestBean2 { private String name; // standard getters and setters } @Bean public TestBean2 testBean(){ return new TestBean2(); } } 

Successivamente, creeremo la nostra classe di test Spring Boot:

@RunWith(SpringRunner.class) @SpringBootTest(classes = {TestConfiguration1.class, TestConfiguration2.class}) public class SpringBootBeanDefinitionOverrideExceptionIntegrationTest { @Test public void whenBeanOverridingAllowed_thenTestBean2OverridesTestBean1() { Object testBean = applicationContext.getBean("testBean"); assertThat(testBean.getClass()).isEqualTo(TestConfiguration2.TestBean2.class); } } 

L'esecuzione del test produce un'eccezione BeanDefinitionOverrideException . Tuttavia, l'eccezione ci fornisce alcune informazioni utili:

Invalid bean definition with name 'testBean' defined in ... ... com.baeldung.beandefinitionoverrideexception.TestConfiguration2 ... Cannot register bean definition [ ... defined in ... ... com.baeldung.beandefinitionoverrideexception.TestConfiguration2] for bean 'testBean' ... There is already [ ... defined in ... ... com.baeldung.beandefinitionoverrideexception.TestConfiguration1] bound. 

Si noti che l'eccezione rivela due importanti informazioni.

Il primo è il nome del bean in conflitto, testBean :

Invalid bean definition with name 'testBean' ... 

E il secondo ci mostra il percorso completo delle configurazioni interessate:

... com.baeldung.beandefinitionoverrideexception.TestConfiguration2 ... ... com.baeldung.beandefinitionoverrideexception.TestConfiguration1 ... 

Di conseguenza, possiamo vedere che due diversi bean vengono identificati come testBean causando un conflitto. Inoltre, i bean sono contenuti nelle classi di configurazione TestConfiguration1 e TestConfiguration2 .

6. Possibili soluzioni

A seconda della nostra configurazione, gli Spring Beans hanno nomi predefiniti a meno che non li impostiamo esplicitamente.

Pertanto, la prima soluzione possibile è rinominare i nostri fagioli.

Ci sono alcuni modi comuni per impostare i nomi dei bean in primavera.

6.1. Modifica dei nomi dei metodi

Per impostazione predefinita, Spring prende il nome dei metodi annotati come nomi di bean .

Pertanto, se abbiamo dei bean definiti in una classe di configurazione, come il nostro esempio, la semplice modifica dei nomi dei metodi impedirà l' eccezione BeanDefinitionOverrideException :

@Bean public TestBean1 testBean1() { return new TestBean1(); } 
@Bean public TestBean2 testBean2() { return new TestBean2(); } 

6.2. @Bean Annotation

L' annotazione @Bean di Spring è un modo molto comune per definire un bean.

Quindi, un'altra opzione è impostare la proprietà name dell'annotazione @Bean :

@Bean("testBean1") public TestBean1 testBean() { return new TestBean1(); } 
@Bean("testBean2") public TestBean1 testBean() { return new TestBean2(); } 

6.3. Annotazioni stereotipate

Un altro modo per definire un bean è con annotazioni stereotipate. Con la funzione @ComponentScan di Spring abilitata, possiamo definire i nostri nomi di bean a livello di classe utilizzando l' annotazione @Component :

@Component("testBean1") class TestBean1 { private String name; // standard getters and setters } 
@Component("testBean2") class TestBean2 { private String name; // standard getters and setters } 

6.4. Beans Coming From 3rd Party Libraries

In some cases, it's possible to encounter a name conflict caused by beans originating from 3rd party spring-supported libraries.

When this happens, we should attempt to identify which conflicting bean belongs to our application, to determine if any of the above solutions can be used.

However, if we are unable to alter any of the bean definitions, then configuring Spring Boot to allow bean overriding can be a workaround.

To enable bean overriding, let's set the spring.main.allow-bean-definition-overriding property to true in our application.properties file:

spring.main.allow-bean-definition-overriding=true 

By doing this, we are telling Spring Boot to allow bean overriding without any change to bean definitions.

Come nota finale, dobbiamo essere consapevoli del fatto che è difficile indovinare quale bean avrà la priorità perché l'ordine di creazione del bean è determinato dalle relazioni di dipendenza per lo più influenzate in runtime . Pertanto, consentire l'override dei bean può produrre un comportamento imprevisto a meno che non conosciamo abbastanza bene la gerarchia delle dipendenze dei nostri bean.

7. Conclusione

In questo tutorial, abbiamo spiegato cosa significa BeanDefinitionOverrideException in Spring, perché appare all'improvviso e come affrontarlo dopo l'aggiornamento Spring Boot 2.1.

Come sempre, il codice sorgente completo di questo articolo può essere trovato su GitHub.