«1. Обзор
Мы можем использовать возможности механизма Spring DI, используя аннотации в пакетах org.springframework.beans.factory.annotation и org.springframework.context.annotation.
Мы часто называем это «основными аннотациями Spring» и рассмотрим их в этом руководстве.
2. Аннотации, относящиеся к DI
2.1. @Autowired
Мы можем использовать @Autowired, чтобы отметить зависимость, которую Spring собирается разрешить и внедрить. Мы можем использовать эту аннотацию с конструктором, сеттером или внедрением поля.
Внедрение конструктора:
class Car {
Engine engine;
@Autowired
Car(Engine engine) {
this.engine = engine;
}
}
Внедрение сеттера:
class Car {
Engine engine;
@Autowired
void setEngine(Engine engine) {
this.engine = engine;
}
}
Внедрение поля:
class Car {
@Autowired
Engine engine;
}
@Autowired имеет логический аргумент, называемый required, со значением по умолчанию true. Он настраивает поведение Spring, когда не находит подходящего bean-компонента для подключения. При значении true генерируется исключение, в противном случае ничего не подключается.
Обратите внимание, что если мы используем внедрение конструктора, все аргументы конструктора являются обязательными.
Начиная с версии 4.3, нам не нужно явно аннотировать конструкторы с помощью @Autowired, если только мы не объявим хотя бы два конструктора.
Для получения дополнительной информации посетите наши статьи о @Autowired и внедрении конструктора.
2.2. @Bean
@Bean отмечает фабричный метод, который создает экземпляр Spring bean:
@Bean
Engine engine() {
return new Engine();
}
Spring вызывает эти методы, когда требуется новый экземпляр возвращаемого типа.
Полученный компонент имеет то же имя, что и фабричный метод. Если мы хотим назвать его по-другому, мы можем сделать это с аргументами имени или значения этой аннотации (значение аргумента является псевдонимом для имени аргумента):
@Bean("engine")
Engine getEngine() {
return new Engine();
}
Обратите внимание, что все методы, аннотированные с помощью @Bean должны быть в классах @Configuration.
2.3. @Qualifier
Мы используем @Qualifier вместе с @Autowired для предоставления идентификатора компонента или имени компонента, которые мы хотим использовать в неоднозначных ситуациях.
Например, следующие два bean-компонента реализуют один и тот же интерфейс:
class Bike implements Vehicle {}
class Car implements Vehicle {}
Если Spring необходимо внедрить bean-компонент Vehicle, он получает несколько совпадающих определений. В таких случаях мы можем указать имя компонента явно, используя аннотацию @Qualifier.
Использование внедрения конструктора:
@Autowired
Biker(@Qualifier("bike") Vehicle vehicle) {
this.vehicle = vehicle;
}
Использование внедрения сеттера:
@Autowired
void setVehicle(@Qualifier("bike") Vehicle vehicle) {
this.vehicle = vehicle;
}
Альтернативно:
@Autowired
@Qualifier("bike")
void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;
}
Использование внедрения поля:
@Autowired
@Qualifier("bike")
Vehicle vehicle;
Более подробное описание см. здесь статья.
2.4. @Required
@Required в методах установки, чтобы пометить зависимости, которые мы хотим заполнить через XML:
@Required
void setColor(String color) {
this.color = color;
}
<bean class="com.baeldung.annotations.Bike">
<property name="color" value="green" />
</bean>
В противном случае будет выброшено исключение BeanInitializationException.
2.5. @Value
Мы можем использовать @Value для ввода значений свойств в bean-компоненты. Он совместим с конструктором, сеттером и внедрением полей.
Engine(@Value("8") int cylinderCount) {
this.cylinderCount = cylinderCount;
}
Внедрение конструктора:
@Autowired
void setCylinderCount(@Value("8") int cylinderCount) {
this.cylinderCount = cylinderCount;
}
Внедрение сеттера:
@Value("8")
void setCylinderCount(int cylinderCount) {
this.cylinderCount = cylinderCount;
}
Альтернативно:
@Value("8")
int cylinderCount;
Внедрение поля:
Конечно, внедрение статических значений бесполезно. Поэтому мы можем использовать строки-заполнители в @Value для связывания значений, определенных во внешних источниках, например, в файлах .properties или .yaml.
engine.fuelType=petrol
Предположим, что у нас есть следующий файл .properties:
@Value("${engine.fuelType}")
String fuelType;
Мы можем внедрить значение engine.fuelType со следующим:
Мы можем использовать @Value даже со SpEL. Более сложные примеры можно найти в нашей статье о @Value.
2.6. @DependsOn
Мы можем использовать эту аннотацию, чтобы заставить Spring инициализировать другие bean-компоненты перед аннотированным. Обычно это поведение является автоматическим, основанным на явных зависимостях между bean-компонентами.
Нам нужна эта аннотация только тогда, когда зависимости являются неявными, например, загрузка драйвера JDBC или инициализация статической переменной.
@DependsOn("engine")
class Car implements Vehicle {}
Мы можем использовать @DependsOn для зависимого класса, указав имена компонентов зависимостей. Аргумент значения аннотации нуждается в массиве, содержащем имена bean-компонентов зависимостей:
@Bean
@DependsOn("fuel")
Engine engine() {
return new Engine();
}
В качестве альтернативы, если мы определяем bean-компонент с аннотацией @Bean, фабричный метод должен быть аннотирован с помощью @DependsOn:
2.7. @Lazy
Мы используем @Lazy, когда хотим лениво инициализировать наш компонент. По умолчанию Spring с готовностью создает все одноэлементные компоненты при запуске/загрузке контекста приложения.
«Однако бывают случаи, когда нам нужно создать bean-компонент, когда мы его запрашиваем, а не при запуске приложения.
-
Эта аннотация ведет себя по-разному в зависимости от того, где именно мы ее размещаем. Мы можем поместить его:
аннотированный метод фабрики компонентов @Bean, чтобы отложить вызов метода (следовательно, создание компонента) класс @Configuration и все содержащиеся методы @Bean будут затронуты классом @Component, который не является @Configuration, этот bean-компонент будет лениво инициализирован конструктором, сеттером или полем @Autowired для ленивой загрузки самой зависимости (через прокси)
Эта аннотация имеет аргумент с именем value со значением по умолчанию true. Полезно переопределить поведение по умолчанию.
@Configuration
@Lazy
class VehicleFactoryConfig {
@Bean
@Lazy(false)
Engine engine() {
return new Engine();
}
}
Например, пометка bean-компонентов для быстрой загрузки, когда глобальная настройка имеет значение lazy, или настройка конкретных методов @Bean для быстрой загрузки в классе @Configuration, отмеченном @Lazy:
Для получения дополнительной информации посетите веб-сайт эта статья.
2.8. @Lookup
Метод, аннотированный @Lookup, сообщает Spring, что нужно вернуть экземпляр возвращаемого типа метода, когда мы его вызываем.
Подробную информацию об аннотации можно найти в этой статье.
2.9. @Primary
Иногда нам нужно определить несколько bean-компонентов одного типа. В этих случаях внедрение будет неудачным, потому что Spring понятия не имеет, какой компонент нам нужен.
Мы уже видели вариант решения этого сценария: пометить все точки подключения @Qualifier и указать имя нужного компонента.
@Component
@Primary
class Car implements Vehicle {}
@Component
class Bike implements Vehicle {}
@Component
class Driver {
@Autowired
Vehicle vehicle;
}
@Component
class Biker {
@Autowired
@Qualifier("bike")
Vehicle vehicle;
}
Однако в большинстве случаев нам нужен конкретный компонент и редко другие. Мы можем использовать @Primary, чтобы упростить этот случай: если мы пометим наиболее часто используемый компонент с помощью @Primary, он будет выбран в неквалифицированных точках внедрения:
В предыдущем примере автомобиль является основным транспортным средством. Поэтому в классе Driver Spring внедряет компонент Car. Конечно, в бине Biker значение поля Vehicle будет объектом Bike, потому что оно квалифицировано.
2.10. @Scope
Мы используем @Scope для определения области действия класса @Component или определения @Bean. Это может быть синглтон, прототип, запрос, сеанс, глобальная сессия или некоторая пользовательская область.
@Component
@Scope("prototype")
class Engine {}
Например:
3. Аннотации конфигурации контекста
Мы можем настроить контекст приложения с помощью аннотаций, описанных в этом разделе.
3.1. @Profile
@Component
@Profile("sportDay")
class Bike implements Vehicle {}
Если мы хотим, чтобы Spring использовал класс @Component или метод @Bean только тогда, когда активен определенный профиль, мы можем пометить его с помощью @Profile. Мы можем настроить имя профиля с аргументом value аннотации:
Подробнее о профилях можно прочитать в этой статье.
3.2. @Import
@Import(VehiclePartSupplier.class)
class VehicleFactoryConfig {}
С этой аннотацией мы можем использовать определенные классы @Configuration без сканирования компонентов. Мы можем предоставить этим классам аргумент значения @Import:
3.3. @ImportResource
@Configuration
@ImportResource("classpath:/annotations.xml")
class VehicleFactoryConfig {}
С помощью этой аннотации мы можем импортировать конфигурации XML. Мы можем указать местоположение файла XML с помощью аргумента местоположения или его псевдонима, аргумента значения:
3.4. @PropertySource
@Configuration
@PropertySource("classpath:/annotations.properties")
class VehicleFactoryConfig {}
С помощью этой аннотации мы можем определить файлы свойств для настроек приложения:
@Configuration
@PropertySource("classpath:/annotations.properties")
@PropertySource("classpath:/vehicle-factory.properties")
class VehicleFactoryConfig {}
@PropertySource использует функцию повторяющихся аннотаций Java 8, что означает, что мы можем помечать ею класс несколько раз:
~ ~~ 3.5. @PropertySources
@Configuration
@PropertySources({
@PropertySource("classpath:/annotations.properties"),
@PropertySource("classpath:/vehicle-factory.properties")
})
class VehicleFactoryConfig {}
Мы можем использовать эту аннотацию для указания нескольких конфигураций @PropertySource:
Обратите внимание, что, начиная с Java 8, мы можем добиться того же с помощью функции повторяющихся аннотаций, как описано выше.
4. Заключение
В этой статье мы рассмотрели наиболее распространенные аннотации ядра Spring. Мы увидели, как настроить связывание компонентов и контекст приложения, а также как пометить классы для сканирования компонентов.