Spring基础七:基于注解配置—依赖注入

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用于构造函数时,含义稍微有一些不一样。

  1. 如果某个构造函数的@Autowire的require属性是true,表bean初始化的时候,必须使用这个构造函数。由于@Autowire默认require=true,所以实际上,只能有一个构造函数添加@Autowire注解,除非声明为@Autowire(require=false)。
  2. 如果bean有单个构造函数,那么没有必要添加@Autowire注解;
  3. 如果有多个构造函数且都没有添加@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并没有对应的等价物,所以实际项目中我们没有必要去用这套注解,所以也就不过多研究了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值