Анотация на пролетта @Import

1. Общ преглед

В този урок ще научим как да използваме анотацията Spring @Import, докато изясняваме как се различава от @ ComponentScan .

2. Конфигурация и боб

Преди да разберем анотацията @Import , трябва да знаем какво е Spring Bean и да имаме основни работни познания за анотацията @ Configuration .

И двете теми са извън обхвата на този урок. И все пак можем да научим за тях в статията ни за Spring Bean и в документацията за Spring.

Да приемем, че вече сме подготвили три зърна - Bird , Cat и Dog - всеки със свой собствен конфигурационен клас.

След това можем да предоставим нашия контекст с тези класове Config :

@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { BirdConfig.class, CatConfig.class, DogConfig.class }) class ConfigUnitTest { @Autowired ApplicationContext context; @Test void givenImportedBeans_whenGettingEach_shallFindIt() { assertThatBeanExists("dog", Dog.class); assertThatBeanExists("cat", Cat.class); assertThatBeanExists("bird", Bird.class); } private void assertThatBeanExists(String beanName, Class beanClass) { Assertions.assertTrue(context.containsBean(beanName)); Assertions.assertNotNull(context.getBean(beanClass)); } }

3. Групиране на конфигурации с @Import

Няма проблем при декларирането на всички конфигурации. Но представете си проблема с контрола на десетки класове конфигурация в различни източници . Трябва да има по-добър начин.

Анотацията @ Import има решение чрез способността си да групира класове за конфигурация :

@Configuration @Import({ DogConfig.class, CatConfig.class }) class MammalConfiguration { }

Сега просто трябва да помним бозайниците :

@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { MammalConfiguration.class }) class ConfigUnitTest { @Autowired ApplicationContext context; @Test void givenImportedBeans_whenGettingEach_shallFindOnlyTheImportedBeans() { assertThatBeanExists("dog", Dog.class); assertThatBeanExists("cat", Cat.class); Assertions.assertFalse(context.containsBean("bird")); } private void assertThatBeanExists(String beanName, Class beanClass) { Assertions.assertTrue(context.containsBean(beanName)); Assertions.assertNotNull(context.getBean(beanClass)); } }

Е, вероятно скоро ще забравим нашата птица , така че нека направим още една група, която да включва всички класове за конфигурация на животни :

@Configuration @Import({ MammalConfiguration.class, BirdConfig.class }) class AnimalConfiguration { }

И накрая, никой не беше изоставен и просто трябва да запомним един клас:

@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { AnimalConfiguration.class }) class AnimalConfigUnitTest { // same test validating that all beans are available in the context }

4. @Import срещу @ComponentScan

Преди да пристъпим към примери за импортиране , нека спрем бързо и го сравним с @ ComponentScan .

4.1. Прилики

И двете анотации могат да приемат всеки клас @Component или @Configuration .

Нека добавим нов @ Компонент с помощта на @ Import :

@Configuration @Import(Bug.class) class BugConfig { } @Component(value = "bug") class Bug { }

Сега, бобът на бъговете се предлага точно като всеки друг боб.

4.2. Концептуална разлика

Просто казано, можем да постигнем един и същ резултат и с двете анотации . И така, има ли разлика между тях?

За да отговорим на този въпрос, нека си спомним, че Spring обикновено насърчава подхода за конвенция над конфигурацията.

Правейки аналогия с нашите анотации, @ ComponentScan е по-скоро конвенция, докато @ Import изглежда като конфигурация .

4.3. Какво се случва в реалните приложения

Обикновено стартираме нашите приложения, използвайки @ ComponentScan в корен пакет, за да може той да намери всички компоненти за нас. Ако използваме Spring Boot, тогава @SpringBootApplication вече включва @ComponentScan и сме готови . Това показва силата на конвенцията.

Сега, нека си представим, че нашето приложение се разраства много. Сега трябва да се справим с боб от различни места, като компоненти, различни структури на пакети и модули, изградени от нас и трети страни.

В този случай добавянето на всичко в контекста рискува да започне конфликти за това кой боб да се използва. Освен това може да получим бавно време за стартиране.

От друга страна, не искаме да пишем @ Import за всеки нов компонент, защото това е контрапродуктивно.

Вземете например нашите животни. Наистина бихме могли да скрием импортирането от контекстната декларация, но все пак трябва да запомним @ Import за всеки клас Config .

4.4. Работим заедно

Можем да се стремим към най-доброто от двата свята. Нека си представим, че имаме пакет само за нашите животни . Това може да бъде компонент или модул и да запази същата идея.

Тогава можем да имаме един @ ComponentScan само за нашия пакет за животни :

package com.baeldung.importannotation.animal; // imports... @Configuration @ComponentScan public class AnimalScanConfiguration { }

И @Import, за да запазите контрол над това, което ще добавим към контекста:

package com.baeldung.importannotation.zoo; // imports... @Configuration @Import(AnimalScanConfiguration.class) class ZooApplication { }

И накрая, всеки нов боб, добавен към опаковката за животни, автоматично ще бъде намерен от нашия контекст. И все още имаме изричен контрол върху конфигурациите, които използваме.

5. Заключение

В този бърз урок научихме как да използваме @Import за организиране на нашите конфигурации.

Също така научихме, че @Import е много подобен на @ ComponentScan , с изключение на факта, че @ Import има изричен подход, докато @ ComponentScan използва имплицитен .

Също така разгледахме възможни трудности при управлението на нашите конфигурации в реални приложения и как да се справим с тях чрез комбиниране на двете анотации.

Както обикновено, пълният код е достъпен в GitHub.