Вступление
Spring Framework - очень надежный фреймворк, выпущенный в 2002 году. Его основные функции могут применяться к простым Java-приложениям или расширяться до сложных современных веб-приложений.
Поскольку он постоянно обновляется и следует новым парадигмам архитектуры и программирования, он предлагает поддержку многих других фреймворков, которые работают с ним рука об руку.
С таким огромным набором функций вполне нормально, что он знакомит нас с некоторыми новыми аннотациями, которые являются ключевой частью разработки приложений Spring.
Конфигурация Spring полностью настраиваема, что изначально было сделано с помощью файлов конфигурации XML . Однако этот подход устарел, и в настоящее время большинство людей прибегают к настройке аннотаций .
При этом эта серия статей направлена на раскрытие возможностей, которые вы, как разработчик, должны настраивать и использовать фреймворк Spring:
- Аннотации Spring Framework: @RequestMapping и его варианты
- Аннотации Spring: основные аннотации
- Аннотации Spring: Аннотации Spring Cloud
- Аннотации 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.