Первоначально опубликовано в блоге SpringSource Джош Лонг
Иногда Twitterz — удивительное место. Буквально на прошлой неделе я провел некоторое время, помогая прояснить поведение @Qualifier
аннотации Spring , которая старше JSR 330 и предлагает более богатый расширенный набор @Qualifier
аннотаций JSR 330 . Эти ошибочные немногие, казалось, находились под впечатлением, что аннотация Spring не обеспечивает такую же степень безопасности типов, как аннотация JSR 330. Я не знаю, потому что они просто не читали о поддержке (что довольно ново, поскольку она существует только с 2007 года) или потому, что они работают на компании, которые зарабатывают деньги, если вы перестанете использовать Spring Но в любом случае это была отличная возможность для повышения квалификации!
Аннотация квалификатора помогает устранить неоднозначность ссылок на bean-компоненты, если Spring не смог бы этого сделать. XML-конфигурация Spring поддерживает такую версию, но, конечно, без безопасности типов. В этом примере мы сосредоточимся на использовании конфигурации Java и компонентного сканирования для регистрации bean-компонентов. По мере того, как все больше людей переходят на 8-летний стиль конфигурации Java в Spring, этот вопрос, похоже, возникает чаще. Spring Boot — это основанный на конфигурации Java подход к созданию приложений, и этот метод может оказаться полезным в более крупном приложении на основе Spring Boot.
Это просто. Предположим, у вас есть два компонента, которые реализуют MarketPlace
интерфейс. Если вы объявите массив MarketPlace
s — тогда Spring предоставит все bean-компоненты, которые реализуют этот интерфейс:
@Autowired private MarketPlace[] marketPlaces;
Если вы хотите ввести только один, вам нужно устранить неоднозначность ссылок. В простом случае вы можете просто сделать это по идентификатору компонента:
@Autowired @Qualifier( "ios") // the use is unique to Spring. It's darned convenient, too! private MarketPlace marketPlace ;
Это предполагает, что вы в другом месте определили компонент, чей ID ios
. Это использование является уникальным для Spring. Вы также можете использовать @Qualifier
для создания безопасной привязки типа, которая связывает определение компонента с сайтом внедрения по качествам аннотации квалификатора. Вот пример, основанный на аннотациях pureplay Spring:
package spring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static spring.Spring.Platform; @Configuration @ComponentScan public class Spring { public static void main(String[] args) { new AnnotationConfigApplicationContext(Spring.class); } @Autowired @Platform(Platform.OperatingSystems.ANDROID) private MarketPlace android; @Autowired @Platform(Platform.OperatingSystems.IOS) private MarketPlace ios; @PostConstruct public void qualifyTheTweets() { System.out.println("ios:" + this.ios); System.out.println("android:" + this.android); } // the type has to be public! @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public static @interface Platform { OperatingSystems value(); public static enum OperatingSystems { IOS, ANDROID } } } interface MarketPlace { } @Component @Platform(Platform.OperatingSystems.IOS) class AppleMarketPlace implements MarketPlace { @Override public String toString() { return "apple"; } } @Component @Platform(Platform.OperatingSystems.ANDROID) class GoogleMarketPlace implements MarketPlace { @Override public String toString() { return "android"; } }
Чтобы скомпилировать и запустить этот пример, убедитесь, что у вас есть org.springframework.boot:spring-boot-starter:1.1.8.RELEASE
CLASSPATH.
В этом примере показано определение двух MarketPlace
реализаций, одна для GoogleMarketPlace
и одна для AppleMarketPlace
. Мы определяем аннотацию, @Platform
которая принимает параметр типа Platform.OperatingSystems
. Эта аннотация сама помечена, @Qualifier
что говорит Spring, чтобы рассматривать ее как классификатор. Определения bean-компонентов помечаются соответствующим образом: с GoogleMarketPlace
пометкой, @Platform(Platform.OperatingSystems.ANDROID)
а с AppleMarketPlace
пометкой @Platform(Platform.OperatingSystems.IOS)
. Впрыскивание любого из них (в Spring
классе) становится таким же простым, как использование @Qualifier
аннотации в месте инъекции. Здесь я использую полевую инъекцию, хотя это всего лишь блокнот для уточнения вещей. Очевидно, что в любом реальном коде вы должны предпочесть внедрение конструктора и сеттера.
Spring также поддерживает JSR 330. В конце концов, мы помогли возглавить эту инициативу. Вот эквивалентный пример с использованием альтернатив JSR 330. @Component
становится @Named
, @Autowired
становится @Inject
и @Qualifier
становится @javax.inject.Qualifier
, но в остальном это должно выглядеть очень знакомо.
package jsr330; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static jsr330.Jsr330.Platform; @Configuration @ComponentScan public class Jsr330 { public static void main(String[] args) { new AnnotationConfigApplicationContext(Jsr330.class); } @Inject @Platform(Platform.OperatingSystems.ANDROID) private MarketPlace android; @Inject @Platform(Platform.OperatingSystems.IOS) private MarketPlace ios; @PostConstruct public void qualifyTheTweets() { System.out.println("ios:" + this.ios); System.out.println("android:" + this.android); } // the type has to be public! @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @javax.inject.Qualifier public static @interface Platform { OperatingSystems value(); public static enum OperatingSystems { IOS, ANDROID } } } interface MarketPlace { } @Named @Platform(Platform.OperatingSystems.IOS) class AppleMarketPlace implements MarketPlace { @Override public String toString() { return "apple"; } } @Named @Platform(Platform.OperatingSystems.ANDROID) class GoogleMarketPlace implements MarketPlace { @Override public String toString() { return "android"; } }
Чтобы скомпилировать и запустить этот пример, убедитесь, что у вас есть org.springframework.boot:spring-boot-starter:1.1.8.RELEASE
и javax.inject:javax.inject:1
на CLASSPATH.
Что-нибудь из этого нового? Нет, дело в этом. Это стало возможным с весны 2.5 (которую мы выпустили в 2007 году). Удивительно, что люди до сих пор не знают об этой функциональности, но, надеюсь, этот блог облегчит людям начало работы. В качестве следующего шага ознакомьтесь с документацией (начиная с версии 2.5!), В которой подробно рассматриваются все подробности, включая альтернативу XML!
Я должен отметить, что — на практике — мне не нужно было делать это много в моем коде. Может быть, дюжину раз за последние 7 лет. Хотя это может быть удобно!