Spring IoC Annotation-based configuration, часть 2
В предыдущей статье я рассказал об основных аннотациях Spring IoC, однако есть еще несколько интересных вещей, о которых хотелось бы поведать.
Для, тех, кто не в курсе, что такое Spring Framework предлагаю почитать вот эту статью.
Еще несколько слов об @Autowired
Аннотация @Autowired может применяться не только к полям бина чтобы заинъектить в них зависимости, но так же к сеттерам, конструкторам и методам. Рассмотрим, что же будет значить @Autowired в каждом из этих случаев.
1. @Autowired примененный к сеттеру
Пусть наш подопытный бин, который мы будет инъектить выглядит так:
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service( «testBean» )
@Scope( «singleton» )
public class TestBean <
private String data = «I am a singleton!!» ;
public String getData() <
return data;
>
public void setData( String data) <
this .data = data;
>
>
* This source code was highlighted with Source Code Highlighter .
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service( «lab1Bean» )
@Scope( «session» )
public class SimpleBean <
private TestBean bean;
@PostConstruct
public void init() <
System. out .println(bean.getData());
>
public void setBean(TestBean bean) <
this .bean = bean;
>
>
* This source code was highlighted with Source Code Highlighter .
@Autowired
public void setBean(TestBean bean) <
this .bean = bean;
>
* This source code was highlighted with Source Code Highlighter .
он заинъектит в поле бина синглтон TestBean’a и в консоли мы увидим заветное «I am a singleton!!».
2. @Autowired примененный к конструктору
Ситуация аналогичная — если мы добавим вот такой конструктор:
@Autowired
public SimpleBean (TestBean bean) <
this .bean = bean;
>
* This source code was highlighted with Source Code Highlighter .
то в консоли увидим долгожданное «I am a singleton!!».
3. @Autowired примененный к методу
Самая интересная ситуация, добавляем вот такой метод
@Autowired
public void doSomething(TestBean bean) <
this .bean = bean;
System. out .println( «I’m an autowired method and » + bean.getData());
>
* This source code was highlighted with Source Code Highlighter .
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service( «lab1Bean» )
@Scope( «session» )
public class SimpleBean <
@Autowired(required= false )
private TestBean bean;
@PostConstruct
public void init() <
// System.out.println(bean.getData());
>
public void setBean(TestBean bean) <
this .bean = bean;
>
>
* This source code was highlighted with Source Code Highlighter .
Аннотация Qualifier
@Autowired
@Qualifier( «specialTestBean» )
private TestBean bean;
* This source code was highlighted with Source Code Highlighter .
Эта конструкция будет искать в контексте бин с именем specialTestBean и в нашем примере мы соответственно получим исключение, так как TestBean объявлен с именем ‘testBean’ (@Service(«testBean»)).
На основе Qualifier можно создавать свои признаки бинов, об этом достаточно хорошо написано (и, что немаловажно, с огромным количеством примеров) в Spring Reference Manual.
Аннотация Resource
// @Autowired
// аналогично
@Resource
private TestBean bean;
* This source code was highlighted with Source Code Highlighter .
// @Autowired
// @Qualifier(«specialTestBean»)
@Resource(name= «specialTestBean» )
private TestBean bean;
* This source code was highlighted with Source Code Highlighter .
На этом про аннотации Spring IoC можно закончить. Надо подвести какие-то выводы.
Spring безусловно стал стройнее, красивее с приходом стиля конфигурирования аннотациями, однако, как показывает практика, механизм пока не отлажен, т.к. иногда случается, что Spring хоть убей не хочет принимать аннотированный бин, а описанный через XML легко проглатывает.
@Qualifier
@Primary – это эффективный способ использования автоматического обнаружения и связывания по типу с несколькими экземплярами, если возможно определить один основной компонент-кандидата. Если вам требуется больше контроля над процессом выборки, вы можете использовать аннотацию @Qualifier в Spring. Вы можете связать значения квалификаторов с конкретными аргументами, сужая набор совпадений по типам таким образом, чтобы для каждого аргумента производилась выборка конкретного бина. В простейшем случае это может быть обычное описательное значение, как показано в следующем примере:
Вы также можете задать аннотацию @Qualifier на отдельные аргументы конструктора или параметры метода, как показано в следующем примере:
В следующем примере показаны соответствующие определения бинов.
- Бин со значением квалификатора main связывается с аргументом конструктора, который имеет такое же уточненное значение.
- Бин со значением квалификатора action связывается с аргументом конструктора, который имеет такое же уточненное значение.
В случае совпадения возврата имя бина считается значением квалификатора по умолчанию. Таким образом, вы можете определить бин с id значения main вместо вложенного элемента классификатора, что приведет к тому же совпадающему результату. Однако, хотя и можно использовать это соглашение для ссылки на конкретные бины по имени, в случае с аннотацией @Autowired речь идет в основном об управлемом типом внедрении зависимости с необязательными семантическими квалификаторами. Это означает, что значения квалификаторов, даже при возврате имени бина, всегда имеют сужающую семантику в пределах набора сопоставления типов. Они не выражают семантически ссылку на уникальный id бина. Надлежащими значениями квалификатора являются main или EMEA , или persistent , выражающие характеристики конкретного компонента, которые не зависят от id бина, который может быть автоматически сгенерирован в случае анонимного определения бина, как в предыдущем примере.
Квалификаторы также применяются к типизированным коллекциям, как обсуждалось ранее, например, к Set<MovieCatalog> . В этом случае все совпадающие бины, согласно объявленным квалификаторам, внедряются как коллекция. Это означает, что квалификаторы не обязательно должны быть уникальными. Скорее, они представляют собой критерии фильтрации. Например, можно определить несколько бинов MovieCatalog с одним и тем же значением классификатора «action», каждый из которых внедряется в Set<MovieCatalog> , аннотированный @Qualifier(«action») .
Предоставление возможности значениям квалификаторов делать выборку по именам целевых бинов в рамках сопоставленных по типу компонентов-кандидатов не требует аннотации @Qualifier в точке внедрения. Если нет иного индикатора разрешения (например, квалификатора или первичного маркера), в случае неуникальной зависимости Spring сопоставляет имя точки внедрения (то есть имя поля или параметра) с именами целевых бинов и выбирает одноименный компонент-кандидата, если таковой имеется.
Тем не менее, если вы намерены выразить управляемое аннотациями внедрение зависимости по имени, не используйте поначалу аннотацию @Autowired , даже если она позволяет делать выборку по имени бина среди сопоставленных по типу компонентов-кандидатов. Вместо этого используйте аннотацию @Resource из JSR-250, которая семантически определена для идентификации конкретного целевого компонента по его уникальному имени, при этом объявленный тип не имеет значения для процесса сопоставления. Аннотация @Autowired имеет совершенно другую семантику: После выборки бинов-кандидатов по типу, указанное значение квалификатора String учитывается только в рамках этих выбранных по типу компонентов-кандидатов (например, сопоставление квалификатора account с бинами, помеченными той же меткой квалификатора).
Для бинов, которые сами определены как тип коллекции, Map или массива, аннотация @Resource является отличным решением, которое ссылается на конкретный бин коллекции или массива по уникальному имени. Тем не менее, начиная с версии 4.3, можно сопоставлять типы коллекций, Map и массивов с помощью алгоритма сопоставления по типу @Autowired в Spring, если информация о типе элемента сохраняется в сигнатурах возвращаемых типов @Bean или в иерархии наследования коллекции. В этом случае можно использовать значения квалификатора для выборки среди однотипных коллекций, как описано в предыдущем пункте.
Начиная с версии 4.3, аннотация @Autowired также учитывает рекурсивные ссылки на внедрение зависимости (то есть ссылки обратно на бин, который в данный момент внедряется). Обратите внимание, что самовнедрение является возвратом. Обычные зависимости от других компонентов всегда имеют приоритет. В этом смысле рекурсивные ссылки не участвуют в выборке обычных компонентов-кандидатов и поэтому, в частности, никогда не являются первичными. Напротив, они всегда имеют наименьший приоритет. На практике рекурсивные ссылки следует использовать только в крайнем случае (например, для вызова других методов для того же экземпляра через транзакционный прокси бина). В таком сценарии следует учесть возможность вынесения затрагиваемых методов в отдельный бин-делегат. В качестве альтернативы можно использовать аннотацию @Resource , которая позволяет вернуть прокси к текущему бину по его уникальному имени.
Попытка внедрить результаты методов @Bean в один и тот же конфигурационный класс также фактически является сценарием с рекурсивной ссылкой. Либо отлаженно разрешайте такие ссылки в сигнатуре метода, когда это действительно необходимо (в отличие от автоматически связанного поля в конфигурационном классе), либо объявляйте затронутые методы аннотации @Bean как статические , отделяя их от содержащего их экземпляра конфигурационного класса и его жизненного цикла. В противном случае такие бины учитываются только на этапе возврата, а в качестве основных компонентов-кандидатов выбираются совпадающие бины других конфигурационных классов (если они доступны).
@Autowired применяется к полям, конструкторам и многоаргументным методам, позволяя сужать их с помощью аннотаций квалификатора на уровне параметров. И наоборот, @Resource поддерживается только для полей и сеттеров свойств бина с одним аргументом. Как следствие, следует использовать квалификаторы, если целью внедрения зависимости является конструктор или многоаргументный метод.
Вы можете создавать свои собственные аннотации квалификаторов. Для этого определите аннотацию и укажите аннотацию @Qualifier в своем определении, как показано в следующем примере:
@Primary, @Qualifier и внедрение списка
Допустим, в Spring приложении есть несколько бинов , реализующих интерфейс IAnimalRepository:
Пусть это два бина: AnimalRepository1 и AnimalRepository2:
Мы внедряем бин типа IAnimalRepository в другой бин — AnimalService:
Возникает вопрос, какой именно бин внедрит Spring. Spring не знает, какую реализацию мы имеем в виду. Если ее не уточнить, то возникнет ошибка:
А ниже — пояснение:
Spring подсказывает, что аннотации @Primary и @Qualifier позволяют уточнить конкретную реализацию.
Аннотация @Primary
Аннотация @Primary задает бин, который будет внедрен по умолчанию (при отсутствии других указаний).
Например, пометим AnimalRepository1 аннотацией @Primary:
Теперь ошибка исчезнет — в AnimalService будет внедрен AnimalRepository1.
Аннотация @Qualifier
Аннотация @Qualifier позволяет уточнить имя бина, который надо внедрить. Используется прямо перед аргументом.
В этот раз давайте скажем Spring внедрить бин AnimalRepository2. Для этого укажем в аннотации @Qualifier имя бина.
На этот раз Spring внедрит бин AnimalRepository2.
Кстати, аннотация @Primary над AnimalRepository1 случайно осталась. Но аннотация @Qualifier имеет больший приоритет, чем @Primary, поэтому все равно внедряется AnimalRepository2.
Внедрение списка
Наконец, создадим еще один сервис ListAnimalService и внедрим в него оба репозитория — списком. Все бины типа IAnimalRepository будут внедрены в поле list:
Проверка
Вызов методов save() сервисов (а из них save() репозиториев с выводом номера репозитория) делаем в @PostConstruct главного класса:
Исходный код
Исходный код примера доступен на GitHub.
Добавить комментарий Отменить ответ
Прошу прощения: на комментарии временно не отвечаю.
Spring @Qualifier annotation tutorial
Spring @Qualifier annotation tutorial shows how to use @Qualifier to differentiate beans in a Spring application.
is a popular Java application framework for creating enterprise applications.
Advertisements Spring @Qualifier annotation
The @Qualifier annotation helps disambiguate bean references when Spring would otherwise not be able to do so.
Spring @Qualifier example
The application has different types of message beans. We differentiate between them with @Qualifier .
This is the project structure.
In the pom.xml file, we have basic Spring dependencies spring-core and spring-context and logging logback-classic dependency.
The exec-maven-plugin is used for executing Spring application from the Maven on the command line.
The logback.xml is a configuration file for the Logback logging library.
The IMessage interface has one method declaration.
The first implementation of the interface gives an information message. The @Qualifier is used to identify the bean.
The second implementation gives a warning message. It is also named with the @Qualifier .
The MessageProducer injects two IMessage beans. To differentiate between them, we use @Qualifier annotations.
This is the main application class. It retrieves the messageProducer bean and call its produce method.