De BeanDefinitionOverrideException in Spring Boot

1. Inleiding

De Spring Boot 2.1-upgrade verraste verschillende mensen met onverwachte gebeurtenissen van BeanDefinitionOverrideException. Het kan sommige ontwikkelaars in verwarring brengen en ertoe leiden dat ze zich afvragen wat er is gebeurd met het overheersende gedrag van de bonen in het voorjaar.

In deze tutorial zullen we dit probleem ontrafelen en kijken hoe we het het beste kunnen aanpakken.

2. Maven afhankelijkheden

Voor ons voorbeeld Maven-project moeten we de Spring Boot Starter-afhankelijkheid toevoegen:

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

3. Bean opheffen

Springbonen worden aangeduid met hun naam in een ApplicationContext.

Dus, bean overriding is een standaardgedrag dat optreedt wanneer we een bean definiëren binnen een ApplicationContext die dezelfde naam heeft als een andere boon. Het werkt door simpelweg de voormalige boon te vervangen in geval van een naamconflict.

Vanaf Spring 5.1 is het BeanDefinitionOverrideException werd geïntroduceerd om ontwikkelaars in staat te stellen automatisch de uitzondering te genereren om onverwachte bean-overschrijving te voorkomen. Standaard is het oorspronkelijke gedrag nog steeds beschikbaar, waardoor bonen kunnen worden overschreven.

4. Configuratiewijziging voor Spring Boot 2.1

Spring Boot 2.1 heeft het overschrijven van bonen standaard uitgeschakeld als een defensieve benadering. Het belangrijkste doel is om Let op de dubbele boonnamen van tevoren om te voorkomen dat bonen per ongeluk worden overschreven.

Daarom, als onze Spring Boot-applicatie afhankelijk is van bean-overriding, is het zeer waarschijnlijk dat het BeanDefinitionOverrideException nadat we de Spring Boot-versie hebben geüpgraded naar 2.1 en later.

In de volgende secties zullen we een voorbeeld bekijken waarbij de BeanDefinitionOverrideException zou gebeuren, en dan zullen we enkele oplossingen bespreken.

5. Identificatie van de bonen in conflict

Laten we twee verschillende veerconfiguraties maken, elk met een testBean () methode, om de BeanDefinitionOverrideException:

@Configuration openbare klasse TestConfiguration1 {klasse TestBean1 {privé String-naam; // standaard getters en setters} @Bean public TestBean1 testBean () {retourneer nieuwe TestBean1 (); }} 
@Configuration openbare klasse TestConfiguration2 {klasse TestBean2 {privé String-naam; // standaard getters en setters} @Bean openbare TestBean2 testBean () {retourneer nieuwe TestBean2 (); }} 

Vervolgens maken we onze Spring Boot-testklasse:

@RunWith (SpringRunner.class) @SpringBootTest (classes = {TestConfiguration1.class, TestConfiguration2.class}) openbare klasse SpringBootBeanDefinitionOverrideExceptionIntegrationTest {@Test openbare leegte whenBeanOverridingAllowed_thenBeanDefinitionOverrideExceptionIntegrationTest (test) assertThat (testBean.getClass ()). isEqualTo (TestConfiguration2.TestBean2.class); }} 

Het uitvoeren van de test levert een BeanDefinitionOverrideException. De uitzondering geeft ons echter wat nuttige informatie:

Ongeldige bean-definitie met naam 'testBean' gedefinieerd in ... ... com.baeldung.beandefinitionoverrideexception.TestConfiguration2 ... Kan bean-definitie niet registreren [... gedefinieerd in ... ... com.baeldung.beandefinitionoverrideexception.TestConfiguration2] voor bean 'testBean' ... Er is al [... gedefinieerd in ... ... com.baeldung.beandefinitionoverrideexception.TestConfiguration1] gebonden. 

Merk op dat de uitzondering twee belangrijke stukjes informatie onthult.

De eerste is de conflicterende boonnaam, testBean:

Ongeldige boondefinitie met naam 'testBean' ... 

En de tweede toont ons het volledige pad van de getroffen configuraties:

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

Als resultaat kunnen we zien dat twee verschillende bonen worden geïdentificeerd als testBean een conflict veroorzaken. Bovendien bevinden de bonen zich in de configuratieklassen Testconfiguratie 1 en Testconfiguratie 2.

6. Mogelijke oplossingen

Afhankelijk van onze configuratie hebben Spring Beans standaardnamen, tenzij we ze expliciet instellen.

Daarom is de eerste mogelijke oplossing om onze bonen te hernoemen.

Er zijn enkele veelgebruikte manieren om boonnamen in het voorjaar in te stellen.

6.1. Methodenamen wijzigen

Standaard, Spring neemt de naam van de geannoteerde methoden als bean-namen.

Daarom, als we bonen hebben gedefinieerd in een configuratieklasse, zoals in ons voorbeeld, zal het simpelweg wijzigen van de namen van de methoden voorkomen dat BeanDefinitionOverrideException:

@Bean openbaar TestBean1 testBean1 () {retourneer nieuwe TestBean1 (); } 
@Bean openbaar TestBean2 testBean2 () {retourneer nieuwe TestBean2 (); } 

6.2. @Boon Annotatie

Lente @Boon annotatie is een veel voorkomende manier om een ​​boon te definiëren.

Een andere optie is dus om de naam eigendom van @Boon annotatie:

@Bean ("testBean1") openbaar TestBean1 testBean () {retourneer nieuwe TestBean1 (); } 
@Bean ("testBean2") openbaar TestBean1 testBean () {retourneer nieuwe TestBean2 (); } 

6.3. Stereotype-aantekeningen

Een andere manier om een ​​boon te definiëren, is met stereotype annotaties. Met de lente @BuienRadarNL functie ingeschakeld, kunnen we onze bean-namen op klasniveau definiëren met behulp van de @Component annotatie:

@Component ("testBean1") class TestBean1 {private String naam; // standaard getters en setters} 
@Component ("testBean2") class TestBean2 {private String naam; // standaard getters en setters} 

6.4. Bonen afkomstig uit bibliotheken van derden

In sommige gevallen, het is mogelijk om een ​​naamconflict tegen te komen dat wordt veroorzaakt door bonen die afkomstig zijn van door veren ondersteunde bibliotheken van derden.

Als dit gebeurt, moeten we proberen vast te stellen welke conflicterende boon bij onze applicatie hoort, om te bepalen of een van de bovenstaande oplossingen kan worden gebruikt.

Als we echter geen van de bean-definities kunnen wijzigen, kan het configureren van Spring Boot om bean-overschrijving toe te staan ​​een tijdelijke oplossing zijn.

Om het overschrijven van bonen in te schakelen, stellen we het spring.main.allow-bean-definition-overriding eigendom aan waar in onze application.properties het dossier:

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

Door dit te doen, vertellen we Spring Boot om het overschrijven van bonen toe te staan ​​zonder de bean-definities te wijzigen.

Ten slotte moeten we ons hiervan bewust zijn het is moeilijk te raden welke bean prioriteit zal hebben omdat de volgorde van het maken van de bean wordt bepaald door afhankelijkheidsrelaties die meestal worden beïnvloed tijdens runtime. Daarom kan het toestaan ​​van het overschrijven van bonen onverwacht gedrag veroorzaken, tenzij we de afhankelijkheidshiërarchie van onze bonen goed genoeg kennen.

7. Conclusie

In deze tutorial hebben we uitgelegd wat BeanDefinitionOverrideException betekent in Spring, waarom het plotseling verschijnt en hoe het moet worden aangepakt na de Spring Boot 2.1-upgrade.

Zoals altijd is de volledige broncode van dit artikel te vinden op GitHub.