1.9.3. Fine-tuning Annotation-based Autowiring with @Primary
在有多个装配候选人的情况下,如果是基于类型进行装配,会出现歧义,可以用@Primary指定相应的bean定义,表示优先使用该候选人进行自动装配。
如下,使用@Primary注解firstMovieCatalog,表示优先使用该bean定义:
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
下面代码在上面配置的作用下,movieCatalog会使用firstMovieCatalog进行自动装配:
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
// ...
}
以上注解配置对应的xml配置如下:
1.9.4. Fine-tuning Annotation-based Autowiring with Qualifiers
@Qualifier注解可以更精确的控制自动装配的对象。下面的示例表示movieCatalog会自动装配Qualifier注解值为main的对象:
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
如果构造函数实例化可以像下面这样定义:
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main")MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
被装配的对象在xml中这样定义:
相当于在被装配对象和自动装配处都指定qualifier的value,如果value值可以两方可以对应上,那么就选择这个bean进行装配,上述例子中,movieCatalog自动装配的就是SimpleMovieCatalog1。
基于注解的配置方式如下:
package examples;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("main")
public class SimpleMovieCatalog1 implements MovieCatalog {
}
如果在自动装配字段上使用@Qualifier注解,但是不提供@Qualifier注解的value值,Spring会默认使用bean name作为value的值(然而,Spring默认就是按照bean name进行匹配的,所以如果用户想按照bean name匹配,则不需要用到@Qualifier注解)。实际上,@Qualifier注解在寻找装配对象的候选人时,依然是按照类型来匹配,也就意味着,可以多个bean拥有相同的Qualifier的value,这种情况下,可以用来装配集合类型,比如Set
自动装配注解配置:
@Autowired
@Qualifier("action")
private Set movieCatalogSet;
候选人的注解配置:
package examples;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class MovieConfiguration {
@Bean
@Primary
@Qualifier("action")
public MovieCatalog firstMovieCatalog() {
return new MovieCatalog() {
@Override
public int hashCode() {
return super.hashCode();
}
};
}
@Bean
@Qualifier("action")
public MovieCatalog secondMovieCatalog() {
return new MovieCatalog() {
@Override
public int hashCode() {
return super.hashCode();
}
};
}
@Bean
@Qualifier("action")
public MovieCatalog qualiferMovieCatalog() {
return new MovieCatalog() {
@Override
public int hashCode() {
return super.hashCode();
}
};
}
}
在自动装配完成后,movieCatalogSet中将包含上面三个@Qualifier("action")注解的MovieCatalog对象。
@Autowired和@Resource注解的使用场景:
@Autowired applies to fields, constructors, and multi-argument methods, allowing for narrowing through qualifier annotations at the parameter level. By contrast, @Resource is supported only for fields and bean property setter methods with a single argument. As a consequence, you should stick with qualifiers if your injection target is a constructor or a multi-argument method.
Spring还支持自定义@Qualifier注解:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
用法跟@Qualifier注解相同:
public class MovieRecommender {
@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;
private MovieCatalog comedyCatalog;
@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}
// ...
}
xml配置中将候选人的qualifier type设置成Genre或者类的完全限定名,value还跟之前的意义相同:
在某些情况下,不带value值的自定义注解就足够用了,那么直接如下定义即可:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
public class MovieRecommender {
@Autowired
@Offline
private MovieCatalog offlineCatalog;
// ...
}
Spring还支持自定义@Qualifier注解时新增属性:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
String genre();
Format format();
}
public enum Format {
VHS, DVD, BLURAY
}
这样可以做更加细粒度的控制:
public class MovieRecommender {
@Autowired
@MovieQualifier(format=Format.VHS, genre="Action")
private MovieCatalog actionVhsCatalog;
@Autowired
@MovieQualifier(format=Format.VHS, genre="Comedy")
private MovieCatalog comedyVhsCatalog;
@Autowired
@MovieQualifier(format=Format.DVD, genre="Action")
private MovieCatalog actionDvdCatalog;
@Autowired
@MovieQualifier(format=Format.BLURAY, genre="Comedy")
private MovieCatalog comedyBluRayCatalog;
// ...
}
xml配置候选人时,有两种配置方式(qualifier和meta两种):
1.9.5. Using Generics as Autowiring Qualifiers
使用泛型作为自动装配候选人:
public interface Store {
}
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
@Component
public class StringStore implements Store {
}
@Component
public class IntegerStore implements Store {
}
通过@Autowire实现泛型的自动装配:
@Autowired
private Store s1; // qualifier, injects the stringStore bean
@Autowired
private Store s2; // qualifier, injects the integerStore bean
1.9.6. Using CustomAutowireConfigurer
之前通过继承@Qualifier注解实现自定义@Qualifier注解,本节提供一种方式,在不继承@Qualifier注解的情况下,同样可以自定义qualifier annotation type。通过以下方式注册任意注解后,该注解就可以支持qualifier annotation type:
examples.CustomQualifier
The AutowireCandidateResolver determines autowire candidates by(AutowireCandidateResolver决定装配对象的流程):
- The autowire-candidate value of each bean definition
- Any default-autowire-candidates patterns available on the
element - The presence of @Qualifier annotations and any custom annotations registered with the CustomAutowireConfigurer
1.9.7. Injection with @Resource
@Resource是JSR-250支持的注解,可以用来注解字段或者setter方法。Spring也支持@Resource注解。
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
@Resource注解的属性是name,name指定bean的名称,所以@Resource是autowire by name的,如果不指定name,默认从setter方法或者字段推导出相应的name。
推导规则如下:
If no name is explicitly specified, the default name is derived from the field name or setter method. In case of a field, it takes the field name. In case of a setter method, it takes the bean property name.
大多数情况下,@Resource和@Autowired功能类似:
public class MovieRecommender {
@Resource
private CustomerPreferenceDao customerPreferenceDao;
@Resource
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
1.9.8. Using @PostConstruct and @PreDestroy
javax.annotation.PostConstruct 和javax.annotation.PreDestroy也是JSR-250的标准。Spring同样支持这两个注解。
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}
@PostConstruct,@PreDestroy和@Resource都是由CommonAnnotationBeanPostProcessor处理的
@PostConstruct,@PreDestroy和@Resource注解在JAVA9之后已经删除,如果需要引用,需要到maven下载第三方包