Bean的依赖注入可以全部通过注解来完成。Spring定义了很多注解,同时也不断添加对JSR注解的支持,比如@PostConstrut和@PreDestroy是JSR-330定义的注解。Spring的理念是强调框架的非侵入性,即期望业务代码不要过多和Spring框架紧耦合,如果对某个功能,JSR有合适语义的注解定义,Spring一般也会支持。
开启相关注解
java的注解本质上是附加在代码上的标签信息,本身并没有任何功能;这些注解能发挥作用,关键在于背后的BeanPostProcessor。Spring默认并没有创建所有相关的BeanPostProcessor,需要通过一个配置来开启:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=...>
<context:annotation-config/>
</beans>
上面这个配置实际上向容器注册了AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor。
这样一来,下面介绍的这些注解就能发挥作用了。
@Required
该注解主要用于属性setter方法注解,表示该依赖必须被满足,否则在bean的装配过程中抛出异常。
@Component
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
不过,@Required注解在Spring 5.1已经被声明为deprecated,推荐使用基于构造参数的依赖注入。
@Autowired
注解@Autowired和xml配置里面使用autowire有一定的类似性,但也有区别。相同点是:不显示通过bean的名字来指明依赖的bean,而是让容器来自动查找合适的bean;不同点是:xml里面可以指定通过了类型或name来匹配,而注解@Autowired只能通过类型来匹配。
@Autowired这个注解使用非常广泛,为了节省篇幅,下面在一个代码块里面糅合多个用法。
基本用法
public class ExampleBean {
//构造函数
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
//setter方法
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
//其实任意方法都能使用,而且能一次注入多个参数
@Autowired
public void prepare(MovieCatalog movieCatalog,
MovieFinder movieFinder) {
this.movieFinder = movieFinder;
this.customerPreferenceDao = customerPreferenceDao;
}
//还能直接在字段上注入
@Autowired
private MovieCatalog movieCatalog;
//注入一个bean数组,所有类型匹配的bean都会被注入
@Autowired
private MovieCatalog[] movieCatalogs;
//注入一个有明确类型参数的集合,道理和上面一样
@Autowired
private Set<MovieCatalog> movieCatalogs;
//注入所有和map类型匹配的bean,key是bean的名字
@Autowired
private Map<String, MovieCatalog> movieCatalogs;
}
@Autowired是以类型匹配为规则来查找bean,因此能够实现bean普通字段和bean集合字段的注入。其中bean数组注入的顺序是这样决定的:如果bean实现了org.springframework.core.Order接口,以Order值为准;否则以bean定义的顺序为准。
require属性
默认情况下,@Autowire声明的依赖是必须要被满足的(对于bean数组和集合,至少得有一个元素);这个规则可以通过require属性来改变。
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required = false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
这表示,如果没有合适MovieFinder类型的bean,那么movieFinder=null就好。在Java8里面,还可以通过Optional来达到同样的效果:
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
Spring 5.0以后,@Nullable注解也可以达到同样效果:
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
...
}
}
@Autowire用于构造函数
public class SimpleMovieLister {
@Autowired
public SimpleMovieLister(MovieFinder movieFinder) {
...
}
}
@Autowire用于构造函数时,含义稍微有一些不一样。
- 如果某个构造函数的@Autowire的require属性是true,表bean初始化的时候,必须使用这个构造函数。由于@Autowire默认require=true,所以实际上,只能有一个构造函数添加@Autowire注解,除非声明为@Autowire(require=false)。
- 如果bean有单个构造函数,那么没有必要添加@Autowire注解;
- 如果有多个构造函数且都没有添加@Autowire注解,那么Spring会选择参数最多,且参数都能够被有效注入的构造函数。
@Primary选择bean
由于Autowire机制是通过类型匹配来选择注入的bean,那么如果有多个满足需求的bean,需要有一种选择机制。
第一种就是@Primary,如果一个bean加上了@Primary注解,那么就会被优先选择。
下面的代码里,注入到MovieRecommender.movieCatalog字段的是FirstMovieCatalog:
@Component
@Primary
public class FirstMovieCatalog implements MovieCatalog { ... }
@Component
public class SecondMovieCatalog implements MovieCatalog { ... }
@Component
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
}
@Qualifier选择bean
另一种更精确的方式是通过@Qualifier注解,类似:
@Component
@Qalifier("first")
public class FirstMovieCatalog implements MovieCatalog { ... }
@Component
@Qalifier("second")
public class SecondMovieCatalog implements MovieCatalog { ... }
@Component
public class MovieRecommender {
@Autowired
@Qualifier("first")
private MovieCatalog movieCatalog;
}
一个bean可以附加多个Qualifier,多个bean可以附加同样的Qualifier;如果bean没有任何Qualifier,它的name会被当做一个默认的Qualifier值。
自定义Qualifier
我们还可以定义自己的的Qualifier类型:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
String genre();
Format format();
}
使用方式也是一致的,具体代码请查阅文档,这里不贴了。
泛型bean类型
Java的泛型类型参数,也可以成为选择bean的限定符,比如以下的StringStore实现了Store接口,IntegerStore实现了Store接口:
@Component
public StringStore implements Store<String> {
return new StringStore();
}
@ Component
public IntegerStore implements Store<Integer>() {
return new IntegerStore();
}
@Component
public class ExampleBean() {
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
}
通过@Autowire注入Spring内部对象
前面第四章讲过,Spring提供了一系列xxx-aware接口,将容器内部的一些对象引用通过这些接口暴露给业务bean。有了@Autowire注解,我们基本不需要再实现此类接口了。
public class MovieRecommender {
@Autowire
private ApplicationContext context;
@Autowire
private ApplicationEventPublisherAware aware;
}
@Resource
这个注解是JSR-250提供的,它通过bean的名字实现注入,可以使用在属性定义或setter方法上,示例如下:
public class SimpleMovieLister {
@Resource
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
这个示例的setter方法上,指明了要注入的bean;而属性定义上的@Resource,没有指定名字,这种情况下,使用属性的变量名。
如果@Resource没有指定名字,没有通过默认名字找到合适的bean,那么它接下来就会和@Autowired一样,通过类型匹配来执行注入。
JSR-330标准注解
Spring 3.0以后,提供了对JSR-330关于依赖注入定义的注解,包括@Inject,@Named等。
@Inject
几乎等价与@Autowired:
import javax.inject.Inject;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
不过@Inject不支持required属性,要定义可选的依赖,可以使用Optional或@Nullable:
public class SimpleMovieLister {
@Inject
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
Provider
Provider是Spring ObjectFactory接口的等价物,用于注入作用域更小的bean,或需要延迟加载的场景:
public class SimpleMovieLister {
private Provider<MovieFinder> movieFinder;
@Inject
public void setMovieFinder(Provider<MovieFinder> movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.get().findMovies(...);
...
}
}
@Name和@ManagedBean
@Name和@ManagedBean是等价的,可以定义依赖的bean name,和Qualifier的使用场景类似:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
还可以声明一个类为Spring bean:
@Named("movieListener")
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
@Singleton和@Scope
JSR-330里面提供了@Singleton注解代表单例作用域,JSR的@Scope注解没有什么实际的作用。
另外Spring里面的@Value,@Required,@Lazy,JSR-330并没有对应的等价物,所以实际项目中我们没有必要去用这套注解,所以也就不过多研究了。