Annotation Interface Bean
The names and semantics of the attributes to this annotation are intentionally similar to those of the <bean/> element in the Spring XML schema. For example:
Bean Names
While a name() attribute is available, the default strategy for determining the name of a bean is to use the name of the @Bean method. This is convenient and intuitive, but if explicit naming is desired, the name attribute (or its alias value ) may be used. Also note that name accepts an array of Strings, allowing for multiple names (i.e. a primary bean name plus one or more aliases) for a single bean.
Profile, Scope, Lazy, DependsOn, Primary, Order
Note that the @Bean annotation does not provide attributes for profile, scope, lazy, depends-on or primary. Rather, it should be used in conjunction with @Scope , @Lazy , @DependsOn and @Primary annotations to declare those semantics. For example: The semantics of the above-mentioned annotations match their use at the component class level: @Profile allows for selective inclusion of certain beans. @Scope changes the bean’s scope from singleton to the specified scope. @Lazy only has an actual effect in case of the default singleton scope. @DependsOn enforces the creation of specific other beans before this bean will be created, in addition to any dependencies that the bean expressed through direct references, which is typically helpful for singleton startup. @Primary is a mechanism to resolve ambiguity at the injection point level if a single target component needs to be injected but several beans match by type.
Additionally, @Bean methods may also declare qualifier annotations and @Order values, to be taken into account during injection point resolution just like corresponding annotations on the corresponding component classes but potentially being very individual per bean definition (in case of multiple definitions with the same bean class). Qualifiers narrow the set of candidates after the initial type match; order values determine the order of resolved elements in case of collection injection points (with several target beans matching by type and qualifier).
NOTE: @Order values may influence priorities at injection points, but please be aware that they do not influence singleton startup order which is an orthogonal concern determined by dependency relationships and @DependsOn declarations as mentioned above. Also, Priority is not available at this level since it cannot be declared on methods; its semantics can be modeled through @Order values in combination with @Primary on a single bean per type.
@Bean Methods in @Configuration Classes
Typically, @Bean methods are declared within @Configuration classes. In this case, bean methods may reference other @Bean methods in the same class by calling them directly. This ensures that references between beans are strongly typed and navigable. Such so-called ‘inter-bean references’ are guaranteed to respect scoping and AOP semantics, just like getBean() lookups would. These are the semantics known from the original ‘Spring JavaConfig’ project which require CGLIB subclassing of each such configuration class at runtime. As a consequence, @Configuration classes and their factory methods must not be marked as final or private in this mode. For example:
@Bean Lite Mode
@Bean methods may also be declared within classes that are not annotated with @Configuration . For example, bean methods may be declared in a @Component class or even in a plain old class. In such cases, a @Bean method will get processed in a so-called ‘lite’ mode.
Bean methods in lite mode will be treated as plain factory methods by the container (similar to factory-method declarations in XML), with scoping and lifecycle callbacks properly applied. The containing class remains unmodified in this case, and there are no unusual constraints for the containing class or the factory methods.
In contrast to the semantics for bean methods in @Configuration classes, ‘inter-bean references’ are not supported in lite mode. Instead, when one @Bean -method invokes another @Bean -method in lite mode, the invocation is a standard Java method invocation; Spring does not intercept the invocation via a CGLIB proxy. This is analogous to inter- @Transactional method calls where in proxy mode, Spring does not intercept the invocation — Spring does so only in AspectJ mode.
Bootstrapping
See the @ Configuration javadoc for further details including how to bootstrap the container using AnnotationConfigApplicationContext and friends.
BeanFactoryPostProcessor -returning @Bean methods
Special consideration must be taken for @Bean methods that return Spring BeanFactoryPostProcessor ( BFPP ) types. Because BFPP objects must be instantiated very early in the container lifecycle, they can interfere with processing of annotations such as @Autowired , @Value , and @PostConstruct within @Configuration classes. To avoid these lifecycle issues, mark BFPP -returning @Bean methods as static . For example: By marking this method as static , it can be invoked without causing instantiation of its declaring @Configuration class, thus avoiding the above-mentioned lifecycle conflicts. Note however that static @Bean methods will not be enhanced for scoping and AOP semantics as mentioned above. This works out in BFPP cases, as they are not typically referenced by other @Bean methods. As a reminder, an INFO-level log message will be issued for any non-static @Bean methods having a return type assignable to BeanFactoryPostProcessor .
Что такое Spring Bean
IoC-контейнер Spring управляет одним или несколькими бинами. Эти бины создаются с помощью конфигурационных метаданных, которые вы предоставляете контейнеру (например, в виде XML -определений <bean/> ).
В самом контейнере эти определения бинов представлены в виде объектов BeanDefinition , которые содержат (среди прочей информации) следующие метаданные:
Полное имя класса с указанием пакета: как правило, это фактический класс реализации определяемого бина.
Элементы поведенческой конфигурации бина, которые определяют то, как бин должен вести себя в контейнере (область действия, обратные вызовы жизненного цикла и так далее).
Ссылки на другие бины, которые необходимы для его работы. Эти ссылки также называются взаимодействующими объектами (collaborators) или зависимостями (dependencies).
Другие параметры конфигурации, которые необходимо установить во вновь созданном объекте, например, предельный размер пула или количество соединений, используемых в бине, который управляет пулом соединений.
Эти метаданные преобразуются в набор свойств, которые составляют определение каждого бина. В следующей таблице описаны эти свойства:
Режим автоматического обнаружения и связывания
Режим отложенной инициализации
В дополнение к определениям бинов, которые содержат информацию о том, как создать конкретный бин, реализации ApplicationContext также позволяют регистрировать существующие объекты, которые создаются вне контейнера (пользователями). Это возможно осуществить путем обращения к BeanFactory ApplicationContext через метод getBeanFactory() , который возвращает реализацию DefaultListableBeanFactory . DefaultListableBeanFactory поддерживает данную регистрацию через методы registerSingleton(..) и registerBeanDefinition(..) . Однако типичные приложения работают исключительно с бинами, определенными с помощью стандартных метаданных определения бинов.
Метаданные бинов и экземпляры-одиночки, предоставляемые вручную, должны быть зарегистрированы как можно скорее, чтобы контейнер мог правильно обработать их во время автоматического обнаружения и связывания и других этапов интроспекции. Хотя переопределение существующих метаданных и существующих экземпляров-одиночек и поддерживается в некоторой степени, регистрация новых бинов во время выполнения (одновременно с прямым доступом к фабрике) официально не поддерживается и может привести к исключениям одновременного доступа, несовместимому состоянию в контейнере бинов или к обоим этим исключениям.
Именование бинов
Каждый бин имеет один или несколько идентификаторов. Эти идентификаторы должны быть уникальными в пределах контейнера, в котором находится бин. Бины обычно имеют только один идентификатор. Однако, если требуется более одного, дополнительные можно считать псевдонимами.
В конфигурационных метаданных на основе XML используется атрибут id , атрибут name или оба для задания идентификаторов бина. Атрибут id позволяет задать только один идентификатор. Обычно эти имена являются буквенно-цифровыми (‘myBean’, ‘someService’ и т.д.), но они могут содержать и специальные символы. Если вы хотите ввести другие псевдонимы для бина, то также можете задать их в атрибуте name , отделив запятой ( , ), точкой с запятой ( ; ) или пробелом. Историческая справка: в версиях до Spring 3.1 атрибут id был определен как тип xsd:ID , что ограничивало применение символов. Начиная с версии 3.1, он определяется как тип xsd:string . Обратите внимание, что уникальность id бина все еще обеспечивается контейнером, но уже не анализаторами XML.
Не нужно указывать name или id для бина. Если явно не задать name или id , контейнер сгенерирует уникальное имя для этого бина. Однако если вы хотите ссылаться на этот бин по имени, используя элемент ref или поиск в стиле локатора служб (Service Locator), то должны указать имя. Мотивы отказа от указания имени связаны с использованием внутренних бинов и взаимодействующих объектов с автоматическим обнаружением и связыванием.
Соглашение заключается в использовании типового соглашения Java для именования полей экземпляра при именовании бинов. То есть имена бинов начинаются со строчной буквы и продолжаются camelCase (верблюжьем) регистре. Примерами таких имен являются accountManager , accountService , userDao , loginController и так далее.
Последовательное именование бинов делает конфигурацию более легкой для чтения и понимания. Кроме того, если вы пользуетесь АОП в Spring, то это очень помогает при применении советов (advice) к набору бинов, связанных по имени.
Присвоение псевдонима бину вне определения бина
В самом определении бина можно указать более одного имени для бина, используя комбинацию из одного имени, указанного атрибутом id , и любого количества других имен в атрибуте name . Эти имена могут быть эквивалентными псевдонимами одного и того же бина и полезны в некоторых ситуациях, например, когда каждый компонент в приложении ссылается на общую зависимость, используя имя бина, специфичное для этого компонента.
Однако задание всех псевдонимов, в которых фактических определен бин, не всегда является достаточным. Иногда желательно ввести псевдоним для бина, который определен в другом месте. Это обычно имеет место в больших системах, где конфигурация разделена между каждой подсистемой, а каждая подсистема имеет свой собственный набор определений объектов. В конфигурационных метаданных на основе XML для этого можно использовать элемент <alias/> . В следующем примере показано, как это сделать:
В этом случае бин (в том же контейнере) с именем fromName — после использования этого определения псевдонима — может упоминаться как toName .
Например, конфигурационные метаданные для подсистемы A могут ссылаться на источник данных DataSource под именем subsystemA-dataSource . Конфигурационные метаданные для подсистемы B могут ссылаться на источник данных DataSource под именем subsystemB-dataSource . При создании основного приложения, использующего обе эти подсистемы, основное приложение обращается к источнику данных DataSource под именем myApp-dataSource . Чтобы все три имени ссылались на один и тот же объект, вы можете добавить следующие определения псевдонимов в конфигурационные метаданные:
Теперь каждый компонент и основное приложение могут обращаться к dataSource через имя, которое является уникальным и гарантированно не будет конфликтовать ни с каким другим определением (эффективно создавая пространство имен), но при этом они обращаются к одному и тому же бину.
Если вы используете Javaconfiguration, аннотация @Bean может быть использована для предоставления псевдонимов.
Spring Beans¶
Одно из ключевых понятий в спринге — это Bean. По сути, это просто объект какого-то класса. Эти объекты создаются с помощью метаданных конфигурации, которые предоставляются контейнеру.
A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.
Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.
BeanDefinition¶
В самом контейнере определения bean-компонентов представлены в виде объектов BeanDefinition, которые содержат некоторые метаданные. Эти метаданные преобразуются в набор свойств, которые составляют каждый BeanDefinition:
Имя класса с указанием пакета: обычно это фактический класс реализации определяемого компонента.
Каждый Bean имеет один или несколько идентификаторов. These identifiers must be unique within the container that hosts the bean.
A bean usually has only one identifier. However, if it requires more than one, the extra ones can be considered aliases.
When you create a bean definition, you create a recipe for creating actual instances of the class defined by that bean definition.
Определяет отношение между BeanDefinition и конкретными инстансами Bean-ов (например при указании аргументов конструктора или пропертей (см. следующий пункт)).
The Spring Framework supports six scopes, four of which are available only if you use a web-aware ApplicationContext:
singleton
(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototype
Scopes a single bean definition to any number of object instances.
request
Scopes a single bean definition to the lifecycle of a single HTTP request.
session
Scopes a single bean definition to the lifecycle of an HTTP Session.
application
Scopes a single bean definition to the lifecycle of a ServletContext.
websocket
Scopes a single bean definition to the lifecycle of a WebSocket.
Ссылки на другие bean-компоненты, коллекции, примитивные значения, которые необходимы для работы. These references are also called collaborators or dependencies.
The Spring container can autowire relationships between collaborating beans.
Можно указать autowiring для bean-компонента и, таким образом, можно выбирать, какой именно bean использовать. Описываются следующие autowiring modes:
no
(Default) No autowiring
byName
Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired.
byType
Lets a property be autowired if exactly one bean of the property type exists in the container.
constructor
Аналогичен byType, но применяется к аргументам конструктора.
By default, ApplicationContext implementations eagerly create and configure all singleton beans as part of the initialization process. A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.
When a lazy-initialized bean is a dependency of a singleton bean that is not lazy-initialized.
The container calls afterPropertiesSet() (Initialization method) for the former and destroy() (Destruction method) for the latter to let the bean perform certain actions upon initialization and destruction of your beans.
The Lifecycle interface defines the essential methods for any object that has its own lifecycle requirements (such as starting and stopping some background process)
Annotation-based Bean configuration¶
Аннотации поддерживаются характерными ним BeanPostProcessor. CommonAnnotationBeanPostProcessor позволяет использовать конфигурацию с помощью аннотаций, независимо от выбранного BeanDefinitionReader (например через xml)
Помечает метод в конфигурации или в компоненте (
), как определение бина. Метод впредь будет возвращать конкретное запроксированное представление бина в контексте.
Для данной аннотации присутствуют следующие поля:
Указывает, какое имя будет иметь бин.
Указывает, какой Autowiring mode будет у данного бина (см BeanDefinition).
Указывает, какой метод будет обрабатывать событие инициализации этого бина в контексте (см @PostConstruct ).
Указывает, какой метод будет обрабатывать событие “уничтожения” этого объекта в контексте (см @PreDestroy ).
Аннотированный класс описывает внутренние свойства BeanDefinition. Имя бина может быть задано параметром в @Component("name") .
Сама же декларация бина происходит с помощью аннотации @Bean или с помощью @ComponentScan .
Данная аннотация является базовой для многих других аннотаций в Spring:
(Сервис-слой приложения) Аннотация, объявляющая, что этот класс представляет собой сервис – компонент сервис-слоя.
Сервис является подтипом класса @Component . Использование данной аннотации позволит искать бины-сервисы автоматически.
(Слой представления) Аннотация для маркировки java класса, как класса контроллера. Данный класс представляет собой компонент, похожий на обычный сервлет (HttpServlet) (работающий с объектами HttpServletRequest и HttpServletResponse), но с расширенными возможностями от Spring Framework.
(Доменный слой) Аннотация показывает, что класс функционирует как репозиторий и требует наличия прозрачной трансляции исключений. Преимуществом трансляции исключений является то, что слой сервиса будет иметь дело с общей иерархией исключений от Spring (DataAccessException) вне зависимости от используемых технологий доступа к данным в слое данных.
Аннотация аккумулирует поведение двух аннотаций @Controller и @ResponseBody (показывает что данный метод может возвращать кастомный объект в виде xml, json…).
Помечает метод в компоненте, что он является post-construct , и вызовется в соответствии с Bean Lifecycle: ( afterPropertiesSet() ).
Помечает метод в компоненте, что он является pre-destroy , и вызовется в соответствии с Bean Lifecycle: ( destroy() ).
Beans on which the current bean depends. Any beans specified are guaranteed to be created by the container before this bean.
Указывается как с @Bean , так и с @Component .
Специфицирует Scope бина.
Указывается как с @Bean , так и с @Component .
Аннотированное поле автоматически связывается (позднее связывание при инициализации бина) с конкретным бином в контейнере (по имени/по типу).
Так же можно аннотировать сетер или конструктор — тогда связываться будут параметры сетера или конструктора, при их вызове при инициализации бина.
Indicates that a particular bean should be given preference when multiple beans are candidates to be autowired to a single-valued dependency.
Указывается как с @Bean , так и с @Component .
Специфицирует имя бина для связывания. Может быть также указан с конкретными аргументами конструктора или сеттера.
Дефолтный Qualifier (т.е. Autowired без Qualifier ) — это имя поля.
Инжекшн в спринге вначале отбирает по типам все подходящие бины, далее проверяет по Qualifier и под конец Primary .
Инжекшн нужно указывать для интерфесов, а не для классов, так как создаются динамические прокси (Proxy) (до Spring 5, после используется только cglib, создающий прокси как объект-наследник) для искомых классов, а сохраняют они при этом интерфейсы а не классы (динамические прокси), по которым будут искаться бины.
Помечает бин, что он инициализируется, только при первом обращении (Lazy initialization mode).
Аннотируется с @Bean или с @Component , как и на конфигурации, при этом если помечен компонент — это значит, что сам компонент является Lazy, в противоположность конфигурации — все бины в ней становятся Lazy (см. Annotation-based Configuration ).
Со Spring 4.3 можно поставить с анотацией @Autowired , для пометки того, что инжекшн будет ленивым, т.е. например, это позволит создавать в синглтонах ленивые (по первому вызову) зависимости.
Вычисляет указанное spring-expression и присваивает в аннотированное поле / параметр (функции / конструктора)
Аннотация для создания профилей конфигурации проекта. Может применяться как к бинам так и к конфигурационным классам.
Инверсия контроля (IoC) и внедрение зависимостей (DI)
Инверсия контроля (инверсия управления) — это принцип в разработке программного обеспечения, при котором управление объектами или частями программы передается контейнеру или фреймворку.
Чаще всего этот принцип используется в контексте объектно-ориентированного программирования.
В отличие от традиционного программирования, в котором наш пользовательский код обращается напрямую к библиотекам, IoC позволяет фреймворку контролировать ход программы и обращаться к нашему коду, когда это необходимо. Для этого, фреймворки используют абстракции со встроенным дополнительным поведением. Если мы хотим добавить наше собственное поведение, нам нужно расширить классы фреймворка или подключить наши собственные классы.
Преимущества этой архитектуры:
- отделение выполнения задачи от ее реализации;
- легкое переключение между различными реализациями;
- большая модульность программы;
- более легкое тестирование программы путем изоляции компонента или проверки его зависимостей и обеспечения взаимодействия компонентов через контракты.
Инверсия управления может быть достигнута с помощью различных механизмов, таких как: шаблон проектирования “Стратегия”, шаблон “Локатор служб”, шаблон “Фабрика” и внедрение зависимостей (DI).
Dependency Injection (DI)
Внедрение зависимостей — это шаблон проектирования для реализации IoC, где инвертируемым (переопределяемым) элементом контроля является настройка зависимостей объекта.
Соединение объектов с другими объектами или «внедрение» объектов в другие объекты выполняется контейнером IoC, а не самими объектами.
В Spring Framework инверсия контроля достигается именно внедрением зависимостей.
В Spring Framework инверсия контроля и внедрение зависимостей считаются одним и тем же.
В Spring Framework внедрение зависимостей описывается как процесс, посредством которого объект определяет свои зависимости (то есть другие объекты, с которыми он работает) только через аргументы конструктора, аргументы фабричного метода или свойства, которые устанавливаются в экземпляре объекта после того, как он создан или возвращен из метода фабрики. После чего контейнер IoC внедряет эти зависимости в компонент при его создании.
Мы можем создать зависимость объекта следующим традиционным способом, без использования принципа IoC:
public class Store <
private Item item;
item = new ItemImpl1();
В приведенном выше примере мы создаем экземпляр конкретной реализации интерфейса Item (ItemImpl1) внутри самого класса Store.
Используя DI , мы можем переписать пример без указания конкретной реализации Item, не создавая её внутри нашего объекта, а ожидая её получение извне (от внешнего фреймворка — контейнера IoC):
public class Store <
private Item item;
public Store(Item item) <
В данном случае инверсия контроля — это переход контроля над зависимостями от объекта Store к контейнеру IoC. Объект Store более не контролирует инстанцирование своего поля (зависимости) item, не создаёт этот объект самостоятельно, а делегирует этот процесс внешним силам — контейнеру IoC, который в нашем примере передаёт в конструктор Store любую из реализаций Item.
IoC Container?
В Spring Framework IoC Container отвечает за создание, настройку и сборку объектов, известных как бины, а также за управление их жизненным циклом.
Он (контейнер) представлен интерфейсом ApplicationContext.
Spring Framework предоставляет несколько реализаций интерфейса ApplicationContext:
ClassPathXmlApplicationContext и FileSystemXmlApplicationContext
— для автономных приложений;
— для обычной Java-конфигурации,в качестве аргумента которому передается класс, либо список классов с аннотацией @Configuration , либо с любой другой аннотацией JSR-330, в том числе и @Component .
Контейнер получает инструкции о том, какие объекты создавать, настраивать и собирать, через метаданные конфигурации, которые представлены в виде XML, Java-аннотаций или Java-кода:
- XML — Метаданные считываются из файла с расширением *.xml;
- Java-аннотации — В Spring 2.5 появилась поддержка метаданных конфигурации на основе аннотаций, которая использует данные байт-кода для подключения компонентов. Вместо того, чтобы использовать XML-файл для описания связывания компонентов, разработчик перемещает конфигурацию в сам класс компонента, используя аннотации к соответствующему классу, методу или полю. При этом, сам XML-файл с базовыми настройками остаётся. Контейнер считывает аннотации перед считыванием XML, поэтому, если бин конфигурируется и через аннотации и через XML-файл, то настройки XML переопределят настройки аннотаций.
Java-код — Начиная со Spring 3.0, используя Java-код, а не файлы XML, мы можем определять настройки в специальном классе, помеченном аннотацией @Configuration .
Bean в Spring?
В Spring объекты, образующие основу приложения и управляемые контейнером Spring IoC, называются бинами.
Бин — это объект, который создается, собирается и управляется контейнером Spring IoC.
Иначе говоря, бин — это просто один из множества объектов в вашем приложении.
Бины и их зависимости отражаются в метаданных конфигурации, используемых контейнером.
Аннотация @Bean
@Bean — Это аннотация Spring Framework, она используется над методом для указания того, что данный метод создает, настраивает и инициализирует новый объект, управляемый Spring IoC контейнером.
Позволяет дополнительно определить у бина:
| Параметр | Описание |
|---|---|
| name | имя (уникальный идентификатор) бина; |
| initMethod | имя метода для вызова во время инициализации бина; |
| destroyMethod | имя метода для вызова во время удаления бина из контекста; |
| autowireCandidate | является ли этот бин кандидатом на автоматическое внедрение в другой бин. |
Также методы бинов, вызывая друг друга в таких классах, не будут создавать бины, а будет просто выполняться код метода, ведь в данном случае они отработают не через прокси.
CGLIB (Code Generation Library) — Это библиотека инструментария байтов, используемая во многих средах Java, таких как Hibernate или Spring. Инструментарий байт-кода позволяет манипулировать или создавать классы после фазы компиляции программы.
Hibernate использует CGLIB для генерации динамических прокси.
Например, он не вернет полный объект, хранящийся в базе данных, но вернет инструментальную версию хранимого класса, которая лениво загружает значения из базы данных по требованию.
Прокси — это шаблон проектирования. Создаем и используем его для добавления и изменения функционала уже существующих классов. В таком случае, прокси-объект применяется вместо исходного. Обычно он использует тот же метод, что и оригинальный, и в Java прокси-классы расширяют исходные.
Имена бинов
Имя бина, которое в контейнере является одновременно и его уникальным идентификатором, по умолчанию соответствует имени метода, аннотированного @Bean.
Но если требуется указать иное имя, то можно использовать атрибут name, который принимает String. Однако, атрибут name также может принимать массив String, что позволяет использовать несколько имен.
Первый элемент массива будет являться именем и уникальным идентификатором бина, а остальные будут его псевдонимами.
public MyBean myBean() <
// instantiate and configure MyBean obj
Аннотация @Component
Если хотим, чтобы из этого класса был создан бин, то используем @Component
Именно эту аннотацию ищет Spring Framework, когда сканирует наши классы.
Именем бина будет название класса с маленькой буквы.
Можно указать имя (Id) для создаваемого бина.
Аннотация @Component имеет наследников:
Все они являются частными случаями использования @Component для слоёв DAO, сервиса и контроллера MVC соответственно.
Также эти аннотации могут иметь дополнительный смысл в будущих версиях Spring Framework.
В остальных же случаях достаточно использовать аннотацию @Component.
указывает, что класс содержит бизнес-логику и вызывает методы на уровне хранилища.
Ничем не отличается от классов с @Component.
указывает, что класс выполняет роль хранилища (объект доступа к DAO).
Задача @Repository заключается в том, чтобы отлавливать определенные исключения персистентности и пробрасывать их как одно непроверенное исключение Spring Framework. Для этого Spring оборачивает эти классы в прокси, и в контекст должен быть добавлен класс PersistenceExceptionTranslationPostProcessor.
указывает, что класс выполняет роль контроллера MVC.
DispatcherServlet просматривает такие классы для поиска @RequestMapping.
@RequestMapping используется для мапинга (связывания) с URL для всего класса или для конкретного метода обработчика.
Отличия @Bean и @Component
Аннотация @Component (как и @Service и @Repository) используется для автоматического обнаружения и автоматической настройки бинов в ходе сканирования путей к классам.
Аннотация @Bean используется для явного объявления бина.
A не для того, чтобы Spring делал это автоматически в ходе сканирования путей к классам:
- прописываем вручную метод для создания бина;
- делает возможным объявление бина независимо от объявления класса, что позволяет использовать классы из сторонних библиотек, у которых мы не можем указать аннотацию @Component;
- аннотацией @Bean можно настроить initMethod, destroyMethod, autowireCandidate, делая создание бина более гибким.
Отличия @Service и @Repository
@Service и @Repository являются частными случаями @Component.
Технически они одинаковы , но мы используем ихдля разных целей.
Задача @Repository заключается в том, чтобы отлавливать определенные исключения персистентности и пробрасывать их как одно непроверенное исключение Spring Framework.
Для этого в контекст должен быть добавлен класс PersistenceExceptionTranslationPostProcessor.
Мы помечаем бины аннотацией @Service, чтобы указать, что они содержат бизнес-логику.
Так что нет никакого другого предназначения, кроме как использовать ее на уровне сервиса.
Аннотация @Autowired
Аннотация Spring Framework, которой отмечают конструктор, поле, сеттер-метод или метод конфигурации, указывая, что им обязательно требуется внедрение зависимостей.
Если в контейнере не будет обнаружен необходимый для вставки бин, то будет выброшено исключение, либо можно указать @Autowired(required = false) , означающее, что внедрение зависимости в данном месте необязательно.
Аннотация @Autowired является альтернативой Java-аннотации @Inject, не имеющей required = false (зависимость должна быть обязательно внедрена).
Начиная со Spring Framework 4.3, аннотация @Autowired для конструктора больше не требуется, если целевой компонент определяет только один конструктор.
Однако, если доступно несколько конструкторов и нет основного/стандартного конструктора, по крайней мере один из конструкторов должен быть аннотирован @Autowired, чтобы указать контейнеру, какой из них использовать.
По умолчанию Spring распознает объекты для вставки по типу.
Если в контейнере доступно более одного бина одного и того же типа, будет исключение. Для избежания этого можно указать аннотацию Spring Framework — @Qualifier («fooFormatter»), где fooFormatter — это имя (Id) одного из нескольких бинов одного типа, находящихся в контейнере и доступных для внедрения:
public class FooService <
private Formatter formatter;
Выбор из нескольких бинов
При выборе между несколькими бинами при автоматическом внедрении используется имя поля. Это поведение по умолчанию, если нет других настроек.
public class FooFormatter implements Formatter <
public class BarFormatter implements Formatter <
public class FooService <
private Formatter fooFormatter;
В этом случае Spring определит, что нужно внедрить бин с именем FooFormatter, поскольку имя поля соответствует значению, которое мы использовали в аннотации @Component для этого бина.
Вставка всех бинов
Мы также можем указать Spring предоставить все бины определенного типа из ApplicationContext, добавив аннотацию @Autowired в поле или метод с массивом или коллекцией этого типа:
private MovieCatalog[] movieCatalogs;
private Set <MovieCatalog> movieCatalogs;
public void setMovieCatalogs(Set <MovieCatalog> movieCatalogs) <
Даже коллекции типа Map могут быть подключены автоматически, если тип ключа — String.
Ключами будут имена бинов, а значениями — сами бины:
public class MovieRecommender <
private Map<String, MovieCatalog> movieCatalogs;
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) <
Аннотация @Resource
Java-аннотация@Resource может применяться к классам, полям и методам.
Она пытается получить зависимость: сначала по имени, затем по типу, затем по описанию (Qualifier).
Имя извлекается из имени аннотируемого сеттера или поля, либо берется из параметра name.
При аннотировании классов имя не извлекается из имени класса по умолчанию, поэтому оно должно быть указано явно.
Указав данную аннотацию у полей или методов с аргументом name, в контейнере будет произведен поиск компонентов с данным именем, и в контейнере должен быть бин с таким именем:
private File defaultFile;
Если указать её без аргументов, то Spring Framework может найти бин по типу.
Если в контейнере несколько бинов-кандидатов на внедрение, то нужно использовать аннотацию @Qualifier
private File dependency1;
private File dependency2;
Разница с @Autowired:
- ищет бин сначала по имени, а потом по типу;
- не нужна дополнительная аннотация для указания имени конкретного бина;
- @Autowired позволяет отметить место вставки бина как необязательное @Autowired(required=false);
- при замене Spring Framework на другой фреймворк, менять аннотацию @Resource не нужно.
Аннотация @Inject
Java-аннотация @Inject входит в пакет javax.inject и, чтобы её использовать, нужно добавить зависимость:
Размещается над полями, методами, и конструкторами с аргументами.
@Inject как и @Autowired в первую очередь пытается подключить зависимость по типу, затем по описанию и только потом по имени.
Это означает, что даже если имя переменной ссылки на класс отличается от имени компонента, но они одинакового типа, зависимость все равно будет разрешена:
private ArbitraryDependency fieldInjectDependency;
отличается от имени компонента, настроенного в контексте приложения:
public ArbitraryDependency injectDependency() <
ArbitraryDependency injectDependency = new ArbitraryDependency();
Разность имён injectDependency и fieldInjectDependency не имеет значения, зависимость будет подобрана по типу ArbitraryDependency.
Если в контейнере несколько бинов-кандидатов на внедрение, то нужно использовать аннотацию @Qualifier:
private ArbitraryDependency defaultDependency;
private ArbitraryDependency namedDependency;
При использовании конкретного имени (Id) бина используем @Named:
private ArbitraryDependency yetAnotherFieldInjectDependency;
Аннотация @Lookup
Обычно бины в приложении Spring являтся синглтонами, и для внедрения зависимостей мы используем конструктор или сеттер.
Но бывает и другая ситуация: имеется бин Car – синглтон (singleton bean), и ему требуется каждый раз новый экземпляр бина Passenger. То есть Car – синглтон, а Passenger – так называемый прототипный бин (prototype bean). Жизненные циклы бинов разные. Бин Car создается контейнером только раз, а бин Passenger создается каждый раз новый – допустим, это происходит каждый раз при вызове какого-то метода бина Car. Вот здесь-то и пригодится внедрение бина с помощью Lookup-метода . Оно происходит не при инициализации контейнера, а позднее: каждый раз, когда вызывается метод.
Суть в том, что мы создаём метод-заглушку в бине Car и помечаем его специальным образом – аннотацией @Lookup. Этот метод должен возвращать бин Passenger, каждый раз новый. Контейнер Spring под капотом создаст прокси-подкласс и переопределит этот метод и будет нам выдавать новый экземпляр бина Passenger при каждом вызове аннотированного метода. Даже если в нашей заглушке он возвращает null (а так и надо делать — всё равно этот метод будет переопределен в прокси-подклассе):
public class Car <
public Passenger createPassenger() <
public String drive(String name) <
Passenger passenger = createPassenger();
return «car with » + passenger.getName();
Допустим, в бине есть метод drive(), и при каждом вызове метода drive() бину Car требуется новый экземпляр бина Passenger – сегодня пассажир Петя, завтра – Вася. То есть бин Passenger прототипный. Для получения этого бина надо написать метод-заглушку createPassenger() и аннотировать его с помощью @Lookup.
Контейнер Spring переопределит этот метод-заглушку и будет выдавать при его вызове каждый раз новый экземпляр Passenger.
Переопределяем бин Passenger как прототипный:
public class Passenger <
private String name;
// + геттер и сеттер
Теперь при вызове метода drive() мы можем везти каждый раз нового пассажира. Имя его передаётся в аргументе метода drive(), и затем задается сеттером во вновь созданном экземпляре пассажира.
Можно ли вставить бин в статическое поле?
Spring не позволяет внедрять бины напрямую в статические поля.
public class TestDataInit <
private static OrderItemService orderItemService;
Если вы распечатать TestDataInit.orderItemService, там будет null.
Чтобы исправить это, необходимо создать нестатический сеттер-метод:
public class TestDataInit <
private static OrderItemService orderItemService;
public void setOrderItemService(OrderItemService orderItemService) <
Аннотации @Primary и @Qualifier
@Primary — эта аннотация указывает, какой компонент определенного типа должен внедряться по умолчанию.
public class Config <
public Employee JohnEmployee() <
return new Employee(«John»);
public Employee TonyEmployee() <
return new Employee(«Tony»);
public class DepartmentManager implements Manager <
public String getManagerName() <
return «Department manager»;
public class GeneralManager implements Manager <
public String getManagerName() <
return «General manager»;
Теперь, где будут требоваться бины типа Employee и Manager будут созданы и внедрены TonyEmployee и GeneralManager.
Когда есть несколько бинов одного типа, подходящих для внедрения
Aннотация @Qualifier позволяет указать в качестве аргумента имя конкретного бина, который следует внедрить.
При совместном применении
Аннотация @Qualifier будет иметь над @Primary приоритет.
Как заинжектить примитив?
@Value
Внедрить в поле примитив можно с помощью аннотации @Value на уровне параметров поля или конструктора/метода.
Значения, которые необходимо внедрить аннотацией @Value, определяются в файле свойств (*.properties)
public class CollectionProvider <
Содержимое файла values.properties:
value.from.file=Value got from the file
Внедряем значение value.from.file, равное “Value got from the file”:
private String valueFromFile;
Если из файла не подтянутся значения по тем или иным причинам, то можно указать значения, которые будут внедрены по умолчанию.
В данном примере, если не будет доступен value.from.file, то внедрится значение “some default”:
private String someDefault;
Если нужно внедрить несколько значений, то можно их определить в файле *.properties через запятую и Spring внедрит их как массив:
private String[] valuesArray;
@Value with SpEL
Кроме того, для внедрения значений можно использовать язык SpEL (Spring Expression Language):
private String spelValue;
или со значениями по умолчанию:
private String spelSomeDefault;
Можно использовать значение поля из другого бина.
Предположим, есть бин с именем someBean с полем someValue, равным 10. В поле будет записано число 10:
private Integer someBeanValue;
Можно манипулировать свойствами, чтобы получить список значений.
Получаем список строковых значений A, B и C:
private List<String> valuesList;
@Value with Map
Можно использовать аннотацию @Value для добавления свойств в Map.
Свойство в файле свойств должно быть определено в формате
private Map <String, Integer> valuesMap;
Можем просто внедрить значение по ключу:
private Integer valuesMapKey1;
Если достоверно не известно, содержит ли Map определенный ключ, то нужно выбрать более безопасное выражение, которое не будет генерировать исключение, а установит значение в null, если ключ не найден:
private Integer unknownMapKey;
Можно установить значения по умолчанию для свойств или ключей, которые могут не существовать:
private Map<String, Integer> unknownMap;
private Integer unknownMapKeyWithDefaultValue;
Записи карты также могут быть отфильтрованы перед внедрением.
Предположим, нам нужно получить только те записи, значения которых больше единицы:
private Map<String, Integer> valuesMapFiltered;
Мы также можем использовать аннотацию @Value для добавления всех текущих системных свойств:
private Map>String, String> systemPropertiesMap;
@Value with Constructor
Можно внедрять значения в конструкторе, если оно не найдено, то будет внедрено значение по умолчанию:
public class PriorityProvider <
private String priority;
@Value with Setter
В приведенном коде используется выражение SpEL для добавления списка значений в метод setValues:
public class CollectionProvider <
private List<String> values = new ArrayList<>();
Как заинжектить коллекцию?
Array Injection
Можно вставлять массивы примитивов и ссылочных типов. Со всеми массивами и коллекциями можно использовать внедрение через конструкторы, сеттеры или поля:
public class ArrayExample <
public TestBean testBean() <
return new TestBean();
public String[] strArray() <
public class TestBean <
private String[] stringArray;
public void setStringArray (String[] stringArray) <
Collections Injection
public class ListExample <
public TestBean testBean() <
return new TestBean();
public List<String> strList() <
return Arrays.asList(«two», «three», «four»);
public class TestBean <
private List <String> stringList;
public void setStringList (List<String> stringList) <
Коллекции бинов одного типа
Можно собрать все бины одного типа, находящиеся в контейнере, и внедрить их в коллекцию или массив:
public class SetInjection <
public TestBean testBean() <
return new TestBean();
public RefBean refBean1() <
return new RefBean(«bean 1»);
public RefBean refBean2() <
return new RefBean2(«bean 2»);
public RefBean refBean3() <
return new RefBean3(«bean 3»);
public static class TestBean <
// All bean instances of type RefBean will be injecting here
private Set<RefBean> refBeans;
public static class RefBean <
private String str;
public RefBean(String str) <
Если нужно внедрить вышеупомянутые бины RefBean в Map, то значениями Map будут сами бины, а ключами будут имена бинов:
Использование @Qualifier
Методы класса JavaConfig (те, которые аннотированы @Bean) могут быть объявлены с определенным квалифицирующим типом, используя @Qualifier. Используя параметр ‘name’ у аннотации @Bean, чтобы указать конкретный классификатор для бина, указывается не имя, а идентификатор бина, который должен быть уникальным, потому что все бины хранятся в контейнере в Map.
В случае если необходимо чтобы несколько бинов имели одно и то же имя квалификатора, чтобы их можно было внедрить в одну коллекцию с одним и тем же квалификатором, нужно использовать аннотацию @Qualifier вместе с @Bean вместо элемента name.
public class SetInjection <
public TestBean testBean() <
return new TestBean();
public RefBean refBean1() <
return new RefBean(«bean 1»);
public RefBean refBean2() <
return new RefBean2(«bean 2»);
public RefBean refBean3() <
return new RefBean3(«bean 3»);
public static class TestBean <
private Set<RefBean> refBeans;
Только бины с именами refBean2 и refBean3 попадут в коллекцию, так как у них одинаковые квалификаторы — myRefBean.
Упорядочивание элементов массивов/списков
Бины могут быть упорядочены, когда они вставляются в списки (не Set или Map) или массивы.
Поддерживаются как аннотация @Order, так и интерфейс Ordered.
public class ArrayExample <
public TestBean testBean() <
return new TestBean();
public String refString1() <
return «my string 1»;
public String refString2() <
return «my string 2»;
public String refString3() <
return «my string 3»;
private static class TestBean <
private String[] stringArray;
public void setStringArray (String[] stringArray) <
public String[] getStringArray() <
Массив строк будет выглядеть так:
[my string 2, my string 3, my string 1]

Также можно объявить бин-коллекцию и внедрять её в другие бины:
public class ActionHeroesService <
public class HeroesConfig <
public List<Hero> action() <
List<Hero> result = new ArrayList<>();
Аннотация @Conditional
Часто бывает полезно включить или отключить весь класс @Configuration, @Component или отдельные методы @Bean в зависимости от каких-либо условий.
Аннотация @Conditional указывает, что компонент имеет право на регистрацию в контексте только тогда, когда все условия соответствуют.
- над классами прямо или косвенно аннотированными @Component, включая классы @Configuration;
- над методами @Bean;
- как мета-аннотация при создании наших собственных аннотаций-условий.
Условия проверяются непосредственно перед тем, как должно быть зарегистрировано BeanDefinition компонента, и они могут помешать регистрации данного BeanDefinition. Поэтому нельзя допускать, чтобы при проверке условий мы взаимодействовали с бинами (которых еще не существует), с их BeanDefinition-ами можно.
Условия мы определяем в специально создаваемых нами классах, которые должны имплементировать функциональный интерфейс Condition с одним единственным методом, возвращающим true или false:
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
Создав свой класс и переопределив в нем метод matches() со своей логикой, необходимо передать этот класс в аннотацию @Conditional в качестве параметра:
Для того, чтобы проверить несколько условий, можно передать в @Conditional несколько классов с условиями:
Если класс @Configuration помечен как @Conditional, то на все методы @Bean, аннотации @Import и аннотации @ComponentScan, связанные с этим классом, также будут распространяться указанные условия.
Для детальной настройки классов, аннотированных @Configuration используется интерфейс ConfigurationCondition
В одном классе — одно условие.
Для создания более сложных условий можно использовать классы AnyNestedCondition, AllNestedConditions и NoneNestedConditions.
В Spring Framework имеется множество готовых аннотаций (и связанных с ними склассами-условиями, имплементирующими интерфейс Condition), которые можно применять совместно над одним определением бина:
| Аннотация | Описание |
|---|---|
| ConditionalOnBean | Условие выполняется, в случае если присутствует нужный бин в BeanFactory. |
| ConditionalOnClass | Условие выполняется, если нужный класс есть в classpath. |
| ConditionalOnCloudPlatform | Условие выполняется, когда активна определенная платформа. |
| ConditionalOnExpression | Условие выполняется, когда SpEL выражение вернуло положительное значение. |
| ConditionalOnJava | Условие выполняется, когда приложение запущено с определенной версией JVM. |
| ConditionalOnJndi | Условие выполняется, только если через JNDI доступен определенный ресурс. |
| ConditionalOnMissingBean | Условие выполняется, в случае если нужный бин отсутствует в контейнере. |
| ConditionalOnMissingClass | Условие выполняется, если нужный класс отсутствует в classpath. |
| ConditionalOnNotWebApplication | Условие выполняется, если контекст приложения не является веб контекстом. |
| ConditionalOnProperty | Условие выполняется, если в файле настроек заданы нужные параметры. |
| ConditionalOnResource | Условие выполняется, если присутствует нужный ресурс в classpath. |
| ConditionalOnSingleCandidate | Условие выполняется, если bean-компонент указанного класса уже содержится в контейнере и он единственный. |
| ConditionalOnWebApplication | Условие выполняется, если контекст приложения является веб контекстом. |
Аннотация @ComponentScan
Аннотация @ComponentScan используется вместе с аннотацией @Configuration для указания пакетов, которые мы хотим сканировать на наличие компонентов, из которых нужно сделать бины.
@ComponentScan без аргументов указывает Spring по умолчанию сканировать текущий пакет и все его подпакеты.
Текущий пакет — тот, в котором находится файл конфигурации с этой самой аннотацией @ComponentScan.
В контейнер попадут:
- бин конфигурационного класса;
- бины, объявленные в конфигурационном классе с помощью @Bean;
- все бины из пакета и его подпакетов.
Аннотация @SpringBootApplication включает в себя аннотации @ComponentScan, @SpringBootConfiguration и @EnableAutoConfiguration , но это не мешает разместить её ещё раз отдельно для указания конкретного пакета.
Если указать @ComponentScan с атрибутом basePackages, то это изменит пакет по умолчанию на указанный:
public class SpringComponentScanApp <
Если указать @ComponentScan с атрибутом excludeFilters, то это позволит использовать фильтр и исключить ненужные классы из процесса сканирования:
Аннотация @Profile
Профили — это ключевая особенность Spring Framework, позволяющая нам относить бины к разным профилям (логическим группам), например, dev, test, prod.
Можно активировать разные профили в разных средах, чтобы загрузить только те бины, которые нужны.
Аннотацию @Profile, относит бин к конкретному профилю.
Можно применять на уровне класса или метода.
Аннотация @Profile принимает в качестве аргумента имя одного или нескольких профилей.
Она фактически реализована с помощью гораздо более гибкой аннотации @Conditional.
Пример:
Есть бин, который должен быть активным только во время разработки, но не должен использоваться в продакшене.
Аннотируем этот компонент с профилем «dev» , и он будет присутствовать в контейнере только во время разработки — во время продакшена профиль dev просто не будет активен:
public class DevDatasourceConfig
В качестве быстрого обозначения имена профилей также могут начинаться с оператора NOT, например «!dev», чтобы исключить их из профиля:
public class DevDatasourceConfig
Тут компонент активируется, только если профиль «dev» не активен.
Следующим шагом является активация нужного профиля для того, чтобы в контейнере были зарегистрированы только бины, соответствующие данному профилю. Одновременно могут быть активны несколько профилей.
По умолчанию, если профиль бина не определен, то он относится к профилю «default».
Spring также предоставляет способ установить профиль по умолчанию, когда другой профиль не активен, используя свойство «spring.profiles.default».
В Spring Boot есть возможность иметь один файл настроек application.properties, в котором будут основные настройки для всех профилей, и иметь по файлу настроек для каждого профиля application-dev.properties и application-prod.properties, содержащие свои собственные дополнительные настройки.
ApplicationContext и BeanFactory
Чем отличаются? В каких случаях что стоит использовать?
BeanFactory
BeanFactory — это интерфейс, который предоставляет механизм конфигурации, способный управлять объектами любого типа.
В общем, BeanFactory предоставляет инфраструктуру конфигурации и основные функциональные возможности.
BeanFactory легче по сравнению с ApplicationContext.
ApplicationContext
ApplicationContext является наследником BeanFactory и полностью реализует его функционал, добавляя больше специфических enterprise-функций.
ApplicationContext vs. BeanFactory
- ApplicationContext загружает все бины при запуске, а BeanFactory — по требованию.
ApplicationContext расширяет BeanFactory и предоставляет функции, которые подходят для корпоративных приложений:
- поддержка внедрения зависимостей на основе аннотаций;
- удобный доступ к MessageSource (для использования в интернационализации);
- публикация ApplicationEvent — для бинов, реализующих интерфейс ApplicationListener, с помощью интерфейса ApplicationEventPublisher;
- простая интеграция с функциями Spring AOP.
ApplicationContext поддерживает автоматическую регистрацию BeanPostProcessor и BeanFactoryPostProcessor.
Поэтому всегда желательно использовать ApplicationContext, потому что Spring 2.0 (и выше) интенсивно использует BeanPostProcessor.