Аннотации Spring: аннотации базовой платформы

Введение Spring Framework [https://spring.io/] - очень надежный фреймворк, выпущенный в 2002 году. Его основные функции могут применяться к простым Java-приложениям или расширяться до сложных современных веб-приложений. Поскольку он постоянно обновляется и следует новым парадигмам архитектуры и программирования, он предлагает поддержку многих других фреймворков, которые работают с ним рука об руку. С таким огромным набором функций вполне нормально, что он знакомит нас с некоторыми новыми аннотациями, которые

Вступление

Spring Framework - очень надежный фреймворк, выпущенный в 2002 году. Его основные функции могут применяться к простым Java-приложениям или расширяться до сложных современных веб-приложений.

Поскольку он постоянно обновляется и следует новым парадигмам архитектуры и программирования, он предлагает поддержку многих других фреймворков, которые работают с ним рука об руку.

С таким огромным набором функций вполне нормально, что он знакомит нас с некоторыми новыми аннотациями, которые являются ключевой частью разработки приложений Spring.

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

При этом эта серия статей направлена на раскрытие возможностей, которые вы, как разработчик, должны настраивать и использовать фреймворк Spring:

Примечание . В этой статье предполагается, что вы знакомы с принципом Spring Inversion of Control.

Основные аннотации

Давайте посмотрим на основные аннотации, которые составляют почти все приложения Spring:

@Bean

@Bean - это базовый объект в Spring Framework. Все сводится к JavaBeans - классам, которые объединяют объекты в один. Они представляют собой тип POJO (простой старый объект Java).

Все JavaBeans должны быть Serializable , все поля должны быть закрытыми, все поля должны иметь сеттеры и геттеры, должен быть конструктор без аргументов, а доступ к полям осуществляется исключительно конструктором или методами геттера / сеттера:

 public class Developer implements java.io.Serializable { 
 private int id; 
 private String name; 
 
 public Developer() {} 
 public void setId(int id) {this.id = id;} 
 public int getId() {return id;} 
 public void setName(String name) {this.name = name;} 
 public String getName() {return name;} 
 } 

С точки зрения Spring - beans создаются и управляются контейнером Spring IoC. Это просто экземпляры объектов, которыми управляет Spring.

Чтобы Spring знал, какими экземплярами объектов он должен управлять, мы просто помечаем методы, в которых мы создаем их @Bean аннотацией @Bean.

Когда встречается этот метод, он будет выполнен, а возвращаемое значение будет сохранено в BeanFactory :

 @Configuration 
 public class ConfigurationClass { 
 @Bean 
 public Developer developer() { 
 return new Developer(); 
 } 
 } 

Это то же самое, что использовать старый XML-подход для регистрации bean-компонента:

 <beans> 
 <bean name="developer" class="com.stackabuse.Developer"/> 
 </beans> 

Теперь, чтобы внедрить этот bean-компонент как зависимость в другой bean-компонент, у нас просто есть другой bean-компонент, вызывающий метод bean-компонента разработчика:

 @Configuration 
 public class ConfigurationClass() { 
 
 @Bean 
 public Manager manager() { 
 return new Manager(developer()); 
 } 
 
 @Bean 
 public Developer developer() { 
 return new Developer(); 
 } 
 } 

@Обязательный

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

Если поля не заполнены во время настройки, компонент не сможет инициализироваться, что приведет к исключению, и приложение не сможет построить:

 public class Developer implements java.io.Serializable { 
 private int id; 
 private String name; 
 
 public Developer() {} 
 
 @Required 
 public void setId(int id) { 
 this.id = id; 
 } 
 
 public int getId() { 
 return id; 
 } 
 
 @Required 
 public void setName(String name) { 
 this.name = name; 
 } 
 
 public String getName() { 
 return name; 
 } 
 } 

Чтобы заполнить поле во время настройки таким образом, мы назначаем имена свойств через XML:

 <bean class="com.stackabuse.Develope> 
 <property name="name" value="David"/> 
 </bean> 

@Autowired

@Autowired используется для дальнейшего контроля над внедрением зависимостей. Он используется для подключения одного компонента к другому без создания экземпляра первого.

Опять же, вместо того, чтобы связывать зависимости через XML, что было громоздко, мы просто помечаем наши зависимости как @Autowired . Основываясь на нашем базовом классе, в котором расположены все наши компоненты, Spring выполняет за нас всю проводку.

Чтобы объявить базовый пакет наших компонентов, мы можем просто добавить тег в файл контекста нашего приложения:

 <context:component-scan base-package="com.stackabuse.basePackage"/> 

Все @Component тегами @Component (включая производные, такие как @Service , @Controller и @Repository ) будут зарегистрированы, поскольку bean-компоненты имеют право на автоматическое подключение.

@Autowired в свойствах

Вместо явного императивного создания экземпляра:

 public class ProductController { 
 private ProductService productService = new ProductService(); 
 
 public void someMethod() { 
 List<Product> productList = productService.getProductList(); 
 } 
 } 

Мы используем декларативный подход:

 public class ProductController { 
 
 @Autowired 
 private ProductService productService; 
 
 public void someMethod() { 
 List<Product> productList = productService.getProductList(); 
 } 
 } 

В этой реализации мы никогда не создаем экземпляр ProductService , отделяя его от ProductController если мы хотим его протестировать.

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

Есть и другие варианты @Autowired аннотации @Autowired.

@Autowired на сеттерах

Очень похоже на @Required , мы также можем использовать @Autowired в установщиках:

 public class ProductController { 
 
 private ProductService productService; 
 
 @Autowired 
 public void setProductService(ProductService productService) { 
 this.productService = productService; 
 } 
 } 

При автоматическом подключении сеттера, подобного этому, нет необходимости заполнять его через XML.

Это так называемая инъекция зависимостей на основе установщика .

@Autowired на конструкторах

@Autowired также можно использовать в конструкторах:

 public class ProductService { 
 
 private ProductDao productDao; 
 
 @Autowired 
 public ProductService(ProductDao productDao) { 
 this.productDao = productDao; 
 } 
 } 

Это так называемая инъекция зависимостей на основе конструктора .

Требуемый флаг

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

Если вы не можете гарантировать, что компонент будет доступен, или если он не всегда нужен, вы можете использовать required чтобы пометить его как необязательный:

 public class ProductController { 
 
 @Autowired(required = false) 
 private ProductService productService; 
 } 

Таким образом, если сервисный компонент продукта недоступен, все будет работать без сбоев.

@Qualifier

@Qualifier используется для устранения случаев, когда мы хотели бы автоматически подключить более одного bean-компонента одного типа.

Например, в компании у нас, скорее всего, будет более одного сотрудника, и у каждого сотрудника есть соответствующая должность - разработчик , ведущий разработчик , менеджер , генеральный директор и т. Д.

 @Component 
 public class Developer implements Employee {} 
 
 @Component 
 public class Manager implements Employee {} 

Если бы мы автоматически подключили сотрудника, было бы неоднозначно, какой bean-компонент мы хотим автоматически подключить:

 @Controller 
 public class CompanyController { 
 @Autowired 
 private Employee employee; 
 } 

Мы увидим ошибку:

 org.springframework.beans.factory.NoSuchBeanDefinitionException: 
 No unique bean of type [com.stackabuse.employee] is defined: 
 expected single matching bean but found 2: [developer, manager] 

Чтобы избежать такой ситуации, мы добавляем квалификаторы:

 @Component 
 @Qualifier("developer") 
 public class Developer implements Employee {} 
 
 @Component 
 @Qualifier("manager") 
 public class Manager implements Employee {} 

А при автоподводке:

 @Controller 
 public class CompanyController { 
 @Autowired 
 @Qualifier("developer") 
 private Employee employee; 
 } 

Это проясняет, какой bean-компонент мы хотим автоматически подключить, и код работает нормально.

@ComponentScan

@ComponentScan аннотацией для Spring является аннотация @ComponentScan. Он указывает, какие пакеты содержат аннотированные классы. Таким образом, Spring знает, какими классами ему нужно управлять, и всегда используется вместе с аннотацией @Configuration

Например, у нас есть com.stackabuse.controller , который содержит все наши контроллеры, где каждый класс аннотирован с помощью @Controller . Чтобы Spring знал, что этот пакет содержит компоненты, требующие управления, мы используем @ComponentScan и добавляем пакет.

В противном случае нам пришлось бы регистрировать каждый bean-компонент по отдельности, что было бы громоздко и невозможно масштабировать.

Во многих случаях мы просто определяем один basePackage , содержащий все наши компоненты, например com.stackabuse . Хотя в некоторых случаях мы хотели бы включить несколько basePackages или basePackageClasses :

 @Configuration 
 @ComponentScan(basePackage = "com.stackabuse") 
 public class SomeApplication { 
 // some code 
 } 

Если мы хотим определить несколько базовых пакетов:

 @Configuration 
 @ComponentScan(basePackage = {"com.package1", "com.package2}) 
 public class SomeApplication { 
 // some code 
 } 

Типобезопасной альтернативой basePackages является basePackageClasses :

 @Configuration 
 @ComponentScan(basePackageClasses = Developer.class) 
 public class SomeApplication { 
 // some code 
 } 

Примечание . Если базовый пакет вообще не определен, пакет, в котором расположен класс, будет использоваться в качестве базового пакета.

@Ленивый

По умолчанию компоненты и компоненты инициализируются без промедления. Если мы хотим изменить это поведение, мы можем сделать это с @Lazy аннотации @Lazy.

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

При аннотировании компонент / bean-компонент не будет инициализирован до тех пор, пока на него не будет явным образом ссылаться другой bean-компонент, и это необходимо для бесперебойной работы приложения:

 @Lazy 
 @Bean 
 class SomeResource {} 

Мы также @Configuration класс @Lazy как @Lazy:

 @Lazy 
 @Configuration 
 public class AppConfig { 
 // some code 
 } 

В этом случае все bean-компоненты, определенные в AppConfig , также будут инициализированы лениво.

@ Конфигурация

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

Это одна из причин, по которой разработчики смогли отказаться от использования конфигурации на основе XML, а простота аннотации делает конфигурацию на основе Java предпочтительной.

 @Configuration 
 public class AppConfig { 
 @Bean 
 public SomeBean someBean() { 
 // Instantiation, configuration, returning the bean 
 } 

@Значение

@Value имеет довольно много вариантов использования в Spring и требует отдельной статьи. Я постараюсь быть кратким и охватить наиболее распространенные и очевидные варианты использования в этом.

Его можно использовать для:

  • Назначение значений по умолчанию для полей
  • Чтение переменных среды
  • Использование выражений Spring Expression Language (SpEL)
  • Значения по умолчанию для параметров, если они используются в методе / конструкторе

При этом давайте рассмотрим все эти варианты использования один за другим.

Значения полей по умолчанию

Если вы хотите присвоить полю значение по умолчанию, @Value довольно проста:

 @Value("Hello World!") 
 private String helloString; 

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

Аннотация @Value предназначена для использования со строками. Если вы попытаетесь применить его к другому типу, он будет работать только в том случае, если Spring сможет легко преобразовать между ними два - например, boolean s и int s:

 @Value("true") 
 private boolean accepted; 
 
 @Value("53") 
 private int userId; 

Свойства среды чтения

Предположим, что среди других свойств наш application.properties содержит некоторые переменные среды:

 sa.website_name = Stack Abuse 

Например, давайте прочитаем это свойство и назначим его String в нашем классе конфигурации. Для этого нам также нужно определить источник свойства:

 @PropertySource("classpath:application.properties") 
 @Configuration 
 public class AppConfig { 
 @Value("${sa.website_name}") 
 private String websiteName; 
 } 

Вообще говоря, ${...} используется в Spring как заполнитель свойств. Вы, вероятно, уже знакомы с этим, если баловались технологиями Spring.

Если свойство недоступно или не определено, мы можем столкнуться с проблемой. В этом случае мы можем определить значения по умолчанию для заполнителей, если они не определены должным образом:

 @PropertySource("classpath:application.properties") 
 @Configuration 
 public class AppConfig { 
 @Value("${sa.website_name}:Backup Value") 
 private String websiteName; 
 } 

Таким образом, если sa.website_name не существует, значение, присвоенное Backup Value .

Использование SpEL

Подобно синтаксису заполнителя, Spring Expression Language (SpEL) использует #{...} для хранения выражений:

 @Value("#{systemProperties['java.home']}") 
 private String someValue; 

Если мы решим добавить какие-то свойства, которые могут быть недоступны, мы снова столкнемся с проблемой. Чтобы избежать таких случаев, мы также можем определить «резервные» значения по умолчанию для SpEL:

 @Value("#{systemProperties['unknownproperty'] ?: 'Backup Value'}") 
 private String someValue; 

Значения параметров по умолчанию

При применении к методу @Value присвоит значение по умолчанию всем параметрам метода:

 @Value("Hello") 
 public String hello(String str1, String str2) { 
 return str1 + str2; 
 } 

Этот метод напечатает:

 HelloHello 

С другой стороны, если мы применим @Value как к методу, так и к параметру, параметру будет присвоено новое значение:

 @Value("Hello") 
 public String hello(String str1, @Value("World") String str2) { 
 return str1 + str2; 
 } 

Результатом в этом случае будет:

 HelloWorld 

@Зависит от

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

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

 @Configuration 
 public class AppConfig { 
 @Bean("firstBean") 
 @DependsOn(value = {"secondBean", "thirdBean"}) 
 public FirstBean firstBean() { 
 return new FirstBean(); 
 } 
 
 @Bean("secondBean") 
 public SecondBean secondBean() { 
 return new SecondBean(); 
 } 
 
 @Bean("thirdBean") 
 public ThirdBean thirdBean() { 
 return new ThirdBean(); 
 } 
 } 

Несмотря на то, что FirstBean расположен перед вторым и третьим, мы отметили, что это зависит от создания SecondBean и ThirdBean для правильной работы. Таким образом, Spring сначала определит эти два, а затем FirstBean .

@Начальный

@Primary часто используются вместе с аннотацией Qualifier Он используется для определения компонента «по умолчанию» для автоматического подключения, когда дополнительная информация недоступна.

Он дает приоритет аннотированному bean-компоненту, если существует более одного bean-компонента одного типа, как следует из названия:

 @Component 
 @Qualifier("developer") 
 @Primary 
 public class Developer implements Employee {} 
 
 @Component 
 @Qualifier("manager") 
 public class Manager implements Employee {} 

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

Тем не менее, на этот раз, нам не нужно , чтобы добавить @Qualifier аннотацию к @Autowired аннотаций в качестве основного боба / по умолчанию было объявлено:

 @Controller 
 public class CompanyController { 
 @Autowired 
 private Employee employee; 
 } 

Это создаст экземпляр bean-компонента Developer

@Сфера

@Scope применяется на уровне компонента и определяет его видимость / жизненный цикл. Если применяется вместе с @Component , он определяет область действия экземпляров аннотированного типа. Если используется в @Bean , область действия применяется к возвращаемому экземпляру.

Есть две основные области действия и еще четыре для веб-приложений:

  • одиночка
  • опытный образец
  • запрос
  • сессия
  • заявление
  • веб-сокет

Одиночная область

Если другое имя области не используется, значение по умолчанию - singleton . singleton область гарантирует только один экземпляр возвращаемого экземпляра аннотированного метода. Объект будет сохранен в контейнере Spring и кэширован, что позволит использовать его в любом месте приложения:

 @Bean 
 @Scope("singleton") 
 public CompanyCEO companyCEO() { 
 return new CompanyCEO(); 
 } 

Опытный образец

В противоположность singleton области видимости, применение области прототипа гарантирует создание new экземпляра аннотированного компонента каждый раз, когда мы его запрашиваем.

 @Bean 
 @Scope("prototype") 
 public Developer developer() { 
 return new Developer(); 
 } 

Запрос

Область request гарантирует создание экземпляра одного bean-компонента для каждого HTTP-запроса:

 // This method will be called on every HTTP request 
 @Bean 
 @Scope("request", proxyMode = ScopedProxyMode.TARGET_CLASS) 
 public SetupClass someSetup() { 
 // Run some setup on each http request 
 } 

Альтернативой может быть использование аннотации 4.3 @RequestScope которая по умолчанию включает прокси.

Сессия

Очень похоже на область request область session будет создавать экземпляр аннотированного bean-компонента с жизненным циклом, зависящим от сеанса HTTP.

 // This method will be called on every HTTP session 
 @Bean 
 @Scope("session", proxyMode = ScopedProxyMode.TARGET_CLASS) 
 public SetupClass someSetup() { 
 // Run some setup on each http session 
 } 

Заявление

Область application работает аналогично области singleton . Жизненный цикл bean-компонента с областью действия application ServletContext .

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

 @Scope("application") 
 @Component 
 public class Application {} 

@ApplicationScope с 4.3, вы можете заменить эту аннотацию на @ApplicationScope.

WebSocket

Если мы используем websocket видимости websocket, мы привязываем жизненный цикл нашего bean-компонента к жизненному циклу сеанса WebSocket

При первом вызове bean-компонент создается и сохраняется для дальнейшего использования в том же сеансе:

 // This method will be called on every websocket session 
 @Bean 
 @Scope("websocket", proxyMode = ScopedProxyMode.TARGET_CLASS) 
 public SetupClass someSetup() { 
 // Run some setup on each websocket session 
 } 

Заключение

Фреймворк Spring - это мощный и надежный фреймворк, который действительно изменил правила игры, когда дело доходит до разработки веб-приложений. Среди множества проектов неплохо было бы начать с базовой структуры и опираться на нее.

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

comments powered by Disqus