Перейти к основному содержимому

Весенняя аннотация @Component

· 6 мин. чтения

1. Обзор

В этом руководстве мы подробно рассмотрим аннотацию Spring @Component и связанные с ней области. Мы увидим, как мы можем использовать его для интеграции с некоторыми основными функциями Spring и как воспользоваться его многочисленными преимуществами.

2. Контекст приложения Spring

Прежде чем мы сможем понять значение @Component , нам сначала нужно немного понять Spring ApplicationContext .

Spring ApplicationContext — это место, где Spring содержит экземпляры объектов, которые были идентифицированы для автоматического управления и распространения. Это так называемые бобы.

Управление бинами и возможность внедрения зависимостей — вот некоторые из основных особенностей Spring.

Используя принцип Inversion of Control , Spring собирает экземпляры bean-компонентов из нашего приложения и использует их в нужное время. Мы можем показать зависимости bean-компонентов для Spring без необходимости обрабатывать настройку и создание экземпляров этих объектов.

Возможность использовать такие аннотации, как @Autowired , для внедрения bean-компонентов, управляемых Spring, в наше приложение, является движущей силой для создания мощного и масштабируемого кода в Spring.

Итак, как нам сообщить Spring о bean-компонентах, которыми мы хотим, чтобы он управлял для нас? Мы должны воспользоваться автоматическим обнаружением bean-компонентов Spring, используя аннотации стереотипов в наших классах.

3. @Компонент

@Component — это аннотация, которая позволяет Spring автоматически обнаруживать наши пользовательские bean-компоненты.

Другими словами, без необходимости писать какой-либо явный код Spring будет:

  • Сканируйте наше приложение на наличие классов, аннотированных с помощью @Component
  • Создайте их экземпляры и внедрите в них любые указанные зависимости.
  • Вставьте их туда, где это необходимо

Однако большинство разработчиков предпочитают использовать более специализированные аннотации стереотипов для выполнения этой функции.

3.1. Весенние аннотации стереотипов

Spring предоставил несколько специализированных аннотаций стереотипов: @Controller , @Service и @Repository . Все они обеспечивают ту же функцию, что и @Component .

Все они действуют одинаково, потому что все они представляют собой составленные аннотации с @Component в качестве мета-аннотации для каждого из них. Они похожи на псевдонимы @Component со специализированным использованием и значением вне автоматического обнаружения Spring или внедрения зависимостей.

Если бы мы действительно захотели, мы теоретически могли бы использовать @Component исключительно для наших нужд автоматического обнаружения bean-компонентов. С другой стороны, мы также можем создавать собственные специализированные аннотации , использующие @Component .

Однако есть и другие области Spring, которые специально ищут специализированные аннотации Spring, чтобы обеспечить дополнительные преимущества автоматизации. Таким образом, мы, вероятно, должны просто придерживаться установленных специализаций большую часть времени.

Предположим, у нас есть пример каждого из этих случаев в нашем проекте Spring Boot:

@Controller
public class ControllerExample {
}

@Service
public class ServiceExample {
}

@Repository
public class RepositoryExample {
}

@Component
public class ComponentExample {
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface CustomComponent {
}

@CustomComponent
public class CustomComponentExample {
}

Мы могли бы написать тест, который доказывает, что каждый из них автоматически определяется Spring и добавляется в ApplicationContext :

@SpringBootTest
@ExtendWith(SpringExtension.class)
public class ComponentUnitTest {

@Autowired
private ApplicationContext applicationContext;

@Test
public void givenInScopeComponents_whenSearchingInApplicationContext_thenFindThem() {
assertNotNull(applicationContext.getBean(ControllerExample.class));
assertNotNull(applicationContext.getBean(ServiceExample.class));
assertNotNull(applicationContext.getBean(RepositoryExample.class));
assertNotNull(applicationContext.getBean(ComponentExample.class));
assertNotNull(applicationContext.getBean(CustomComponentExample.class));
}
}

3.2. @ComponentScan

Прежде чем мы полностью полагаемся на @Component , мы должны понять, что это всего лишь обычная аннотация. Аннотация служит для того, чтобы отличить bean-компоненты от других объектов, таких как объекты предметной области.

Однако Spring использует аннотацию @ComponentScan , чтобы фактически собрать их все в свой ApplicationContext .

Если мы пишем приложение Spring Boot, полезно знать, что @SpringBootApplication — это составная аннотация, включающая @ComponentScan . Пока наш класс @SpringBootApplication находится в корне нашего проекта, он будет сканировать каждый @Component , который мы определяем по умолчанию.

Но если наш класс @SpringBootApplication не может находиться в корне нашего проекта или мы хотим сканировать внешние источники, мы можем явно настроить @ComponentScan для поиска в любом указанном нами пакете, если он существует в пути к классам.

Давайте определим bean-компонент @Component вне области видимости :

package com.foreach.component.scannedscope;

@Component
public class ScannedScopeExample {
}

Затем мы можем включить его с помощью явных инструкций в нашу аннотацию @ComponentScan :

package com.foreach.component.inscope;

@SpringBootApplication
@ComponentScan({"com.foreach.component.inscope", "com.foreach.component.scannedscope"})
public class ComponentApplication {
//public static void main(String[] args) {...}
}

Наконец, мы можем проверить, что он существует:

@Test
public void givenScannedScopeComponent_whenSearchingInApplicationContext_thenFindIt() {
assertNotNull(applicationContext.getBean(ScannedScopeExample.class));
}

На самом деле это, скорее всего, произойдет, когда мы хотим сканировать внешнюю зависимость, которая включена в наш проект.

3.3. Ограничения @Component

Есть несколько сценариев, когда мы хотим, чтобы определенный объект стал bean-компонентом, управляемым Spring, когда мы не можем использовать @Component .

Давайте определим объект с аннотацией @Component в пакете вне нашего проекта:

package com.foreach.component.outsidescope;

@Component
public class OutsideScopeExample {
}

Вот тест, который доказывает, что ApplicationContext не включает внешний компонент:

@Test
public void givenOutsideScopeComponent_whenSearchingInApplicationContext_thenFail() {
assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean(OutsideScopeExample.class));
}

Кроме того, у нас может не быть доступа к исходному коду, поскольку он получен из стороннего источника, и мы не можем добавить аннотацию @Component . Или, возможно, мы хотим условно использовать одну реализацию bean над другой в зависимости от среды, в которой мы работаем. В большинстве случаев достаточно автоматического обнаружения, но когда это не так, мы можем использовать @Bean .

4. @Component против @Bean

@Bean также является аннотацией, которую Spring использует для сбора bean-компонентов во время выполнения, но она не используется на уровне класса. Вместо этого мы аннотируем методы с помощью @Bean , чтобы Spring мог сохранить результат метода в виде bean-компонента Spring.

Сначала мы создадим POJO без аннотаций:

public class BeanExample {
}

Внутри нашего класса, аннотированного @Configuration , мы можем создать метод генерации bean-компонента:

@Bean
public BeanExample beanExample() {
return new BeanExample();
}

BeanExample может представлять локальный класс или внешний класс. Это не имеет значения, потому что нам просто нужно вернуть его экземпляр.

Затем мы можем написать тест, который проверяет, что Spring подхватил bean-компонент:

@Test
public void givenBeanComponent_whenSearchingInApplicationContext_thenFindIt() {
assertNotNull(applicationContext.getBean(BeanExample.class));
}

Есть некоторые важные последствия, которые мы должны отметить из-за различий между @Component и @Bean .

  • @Component — это аннотация на уровне класса, а @Bean — на уровне метода, поэтому @Component можно использовать только в том случае, если исходный код класса доступен для редактирования. @Bean всегда можно использовать, но это более многословно.
  • @Component совместим с автоматическим определением Spring, но @Bean требует ручного создания экземпляра класса.
  • Использование @Bean отделяет создание экземпляра компонента от определения его класса. Вот почему мы можем использовать его для превращения даже сторонних классов в компоненты Spring. Это также означает, что мы можем ввести логику, чтобы решить, какой из нескольких возможных вариантов экземпляра компонента использовать.

5. Вывод

Мы только что изучили аннотацию Spring @Component , а также другие важные темы. Во-первых, мы обсудили различные аннотации стереотипов Spring, которые являются просто специализированными версиями @Component .

Затем мы узнали, что @Component ничего не делает, пока его не найдет @ComponentScan .

Наконец, поскольку невозможно использовать @Component для классов, для которых у нас нет исходного кода, мы научились вместо этого использовать аннотацию @Bean .

Все эти и другие примеры кода можно найти на GitHub .