主要讲解的注解如下:
@required注解
(只能修饰setter方法,表示配置阶段就拥有明确的值
)@Autowired注解
(可以在构造函数上,setter方,成员变量上面,注解将多个Bean注入到Array,Set,Map类型)@Primary注解
(配合java配置注解的方式,可以用到方法上
)@Qualifier注解
(成员变量,构造函数的参数中,或者是方法上面)@Resource注解
(修饰在方法和成员变量上,这个是注解是java本身标准的)@PostConstruct和PreDestroy注解
(bean的声明周期回调,bean实例化之后,销毁之前执行,注解是java标准的)
在使用注解进行自动装配之前,需要实现实现打开注解的配置
,需要在xml的配置如下:
比如接下来讲解的注解@Required,本质上是由BeanPostProcessor后置处理器
来进行处理注解的,
基本原理:
继承InstantiationAwareBeanPostProcessorAdapter
,在postProcessPropertyValues()方法中进行校验
,可以具体看下RequiredAnnotationBeanPostProcessor的postProcessPropertyValues处理部分的源码
//注入属性
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
//如果容器缓存中没有指定Bean名称
if (!this.validatedBeanNames.contains(beanName)) {
//如果指定Bean定义中没有设置skipRequiredCheck属性
if (!shouldSkip(this.beanFactory, beanName)) {
List invalidProperties = new ArrayList<>();
//遍历所有属性
for (PropertyDescriptor pd : pds) {
//如果属性添加了@Required注解,且属性集合中不包含指定名称的属性
if (isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
//当前属性为无效的属性
invalidProperties.add(pd.getName());
}
}
//如果无效属性集合不为空
if (!invalidProperties.isEmpty()) {
throw new BeanInitializationException(buildExceptionMessage(invalidProperties, beanName));
}
}
//将Bean名称缓存到容器中
this.validatedBeanNames.add(beanName);
}
//返回经过验证的属性值
return pvs;
}
由于实现PriorityOrdered接口,@required ,执行优先级比@Autowired、@Resource都要低,getOrder()方法获取的值越小,越被先执行。
常用的注解有对应的后置处理器,有如下的关系
:
BeanPostProcessor具体实现类 | 处理注释 | 优先级(数字越低优先级越高) |
---|---|---|
AutowiredAnnotationBeanPostProcessor |
@Autowoired | Integer.MAX -2 |
CommonAnnotationBeanPostProcessor |
@Resource | Integer.MAX -3 |
RequiredAnnotationBeanPostProcessor |
@required | Integer.MAX -1 |
1.@Required注解
@Required注解需要应用到Bean的属性的setter方法上面
,如下面的例子:
/**
* @Project: spring
* @description: 只能在setter方法使用 当指明Required为是,明确这个属性不能为空,
* @author: sunkang
* @create: 2018-09-16 17:32
* @ModificationHistory who when What
**/
public class RequiredAnotation {
private AnotationName anotationName;
public AnotationName getAnotationName() {
return anotationName;
}
@Required
public void setAnotationName(AnotationName anotationName) {
this.anotationName = anotationName;
}
}
这个还需要在xml配置是根据类型来装配,不然无法进行自动装配,当然也可以配合 @Autowired注解来实现
当Bean的属性配置了这个注解时,该Bean的属性必须在配置阶段就拥有明确的值
,通过精确地Bean定义,或者通过自动装载。如果Bean的属性没有配置,容器会抛出异常。这一机制可以避免之后出现的空指针异常问题
2.@Autowired注解
- 构造函数上面使用@Autowired注解:
- 注入到传统的setter方法上面
- 用到任何名字任何参数的方法上面
- 将@Autowired用到成员变量上面
- 将多个Bean注入到Array类型中
- 将多个Bean注入到Set类型中
- 将多个Bean注入到Map类型中,Map的值可以是任何一种容器中的Bean的类型,key当然就是对应Bean的名字。
开发者所定义的Bean可以通过实现org.springframework.core.Ordered接口或者
通过使用@Order或者标准的@Priority注解
来确定注入到array或者list中
的顺序。每个类仅有一个
注解构造函数可以被标记为必须的
,但是非必须的够早函数可以注解多个
。在存在多个非必须注解(required=false)的情况下,Spring会选择一个最贪婪的构造函数(满足最多的依赖的)
。
@Autowired注解要优于@Required注解
具体的配置如下:
* @Project: spring
* @description: @autowired的演示
* @author: sunkang
* @create: 2018-09-16 17:51
* @ModificationHistory who when What
**/
public class Autowiredannotation {
private String name;
/*注入到成员变量*/
@Autowired
private AnotationName anotationName;
/*符合所有的bean,注入到list集合上面*/
@Autowired
private List anotationNameList;
/*符合所有的bean注入到数组里面*/
@Autowired
private AnotationName[] anotationNames;
/*注入到Map集合,String为bean名称,value为具体的bean*/
@Autowired(required = false)
private Map anotationNameMap;
/*注入到setter方法里面*/
@Autowired
public void setAnotationName(AnotationName anotationName) {
this.anotationName = anotationName;
System.out.println("setter方法注入");
}
@Autowired
public Autowiredannotation(AnotationName anotationName) {
this.anotationName=anotationName;
System.out.println("构造方法注入");
}
/*注入到任何名字任何参数的方法上面*/
public Autowiredannotation(@Autowired AnotationName anotationName,String name) {
this.anotationName=anotationName;
this.name = name;
}
}
3.通过@Primary来自动装载
由于通过类型来装载可能导致多个候选者
,通常很有必要来控制选择依赖的过程,一种方式就是通过Spring的@Primary注解。@Primary注解表明当一个Bean需要进行依赖注入的时候,如果有多个候选者可能注入到单值的依赖之中,那么该Bean拥有优先权
。如果只有一个primary的Bean的话,那么这个Bean将成为自动装载的依赖。
-
完全基于java的配置的方式
下面演示java的配置方式进行注入
/**
* @Project: spring
* @description: 标记注解的名称
* @author: sunkang
* @create: 2018-09-16 17:38
* @ModificationHistory who when What
**/
public class AnotationName {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public AnotationName(String name) {
this.name = name;
}
public AnotationName() {
}
}
模拟一个bean,为自动装配模式
**
* @Project: spring
* @description: 一个bean,含有AnotationName的引用
* @Qualifier("second")的优先级比primary的优先级高
*
* @author: sunkang
* @create: 2018-09-16 18:57
* @ModificationHistory who when What
**/
@Component("primaryAnotation")
public class PrimaryAnotation {
@Autowired
// @Qualifier("second")
private AnotationName anotationName;
public AnotationName getAnotationName() {
return anotationName;
}
public void setAnotationName(AnotationName anotationName) {
this.anotationName = anotationName;
}
}
java注解的配置方式,配置有两个候选人等待被注入,一个是修饰为@primary,一个的修饰符为second
/**
* @Project: spring
* @description: java的配置方式,这里可以跟xml的配置方式进行类比
* @author: sunkang
* @create: 2018-09-16 18:38
* @ModificationHistory who when What
**/
@Configuration
//@ImportResource("classpath:anotationbased/spring-primary.xml")
public class PrimaryConfig {
private AnotationName anotationName;
@Primary
@Bean(name="primaryBean")
public AnotationName getAnotationName() {
return new AnotationName("primaryName");
}
@Bean(name="secondBean")
@Qualifier("second")
public AnotationName getSencondAnotationName(){
return new AnotationName("sencondName");
}
}
进行测试,容器用了注解的方式来驱动的
/**
* @Project: spring
* @description: 基于注解方式的容器启动
* @author: sunkang
* @create: 2018-09-16 18:53
* @ModificationHistory who when What
**/
public class PrimayAnnotationTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PrimaryConfig.class);
context.register(PrimaryAnotation.class);
PrimaryAnotation primaryBean = context.getBean("primaryAnotation",PrimaryAnotation.class);
System.out.println(primaryBean.getAnotationName().getName());
}
}
测试结果如下: 可以发现完全通过注解的方式也可以实现依赖注入,当PrimaryAnotation 修饰的配置@Qualifier("second")的注释被打开时,测试结果为sencondName,也就是修饰符为sencond的对象别注入了
primaryName
可以对比一下xml的配置方式:这两种的配置方式是一样
基于xml的配置方式
基于xml配置的测试方法,测试结果与注解一样
/**
* @Project: spring
* @description: xml 的测试方法
* @author: sunkang
* @create: 2018-09-17 22:36
* @ModificationHistory who when What
**/
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("anotationbased/spring-primary.xml");
PrimaryAnotation primaryBean = context.getBean("primaryAnotation",PrimaryAnotation.class);
System.out.println(primaryBean.getAnotationName().getName());
}
}
4.通过限定符的自动装载
@Primary在相同类型的几个实例之间选择选择依赖的时候,是一种很高效的方式。但是如果想要更多的控制自动装载的过程,@Qualifier注解就更为有用了
修饰成员变量
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
@Qualifier注解可以指定到构造函数的参数,或者是方法上面
:
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;
}
// ...
}
开发者也可以定义自己的限定符注解
,代码如下
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
之后就可以将自定义的限定符应用到自动装载的参数上面了,如下:
public class MovieRecommender {
@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;
private MovieCatalog comedyCatalog;
@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}
// ...
}
5.通过CustomAutowireConfigurer
CustomAutowireConfigurer是一个BeanFactoryPostProcessor,这个后置处理器可以注册开发者自己的限定符注解,让开发者的注解不依赖于Spring限定符注解
。
example.CustomQualifier
实际上在CustomAutowireConfigurer的 postProcessBeanFactory中,注入了一个默认QualifierAnnotationAutowireCandidateResolver解析器
AutowireCandidateResolver
通过以下的几种方式来决定自动装载的候选Bean:
- Bean定义中的autowire-candidate的值
- 任何
标签中定义的default-autowire-candidates的值 - @Qualifier注解和任何在CustomAutowireConfigurer中定义的自定义的限定符注解
当多个Bean限定为自动装载的候选时, 前文中提到的primary属性是优先考虑的。
6.@Resource注解
Spring支持JSR-250标准中的@Resource注解来注入实例变量或者setter方法
@Resource需要一个名字的属性,而默认的情况下,Spring会将Bean的名字注入。
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
如果没有明确指定名字的话,默认的名字就是实例变量的变量名,或者Setter方法中解析出来的名字。以实例变量为例,就取变量的名字作为默认的名字,如果是Setter的方法的话,会取Bean属性的名字。所以,如下的例子会注入Bean的名字的movieFinder的Bean实例
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
在使用@Resource而没有明确指定名字的情况,它比较类似@Autowired注解
,@Resource会优先查找类型匹配而不是名字匹配
,也能够解析一些常见的依赖:BeanFactory,ApplicationContext,ResourceLoader,ApplicationEventPublisher以及MessageSource等接口。
7.@PostConstruct和PreDestroy注解
CommonAnnotationBeanPostProcessor
不仅仅识别@Resource
注解,也识别JSR-250标准中的生命周期注解。也针对回调函数的描述了注解外其他的方法实现回调。CommonAnnotationBeanPostProcessor
是注册在Spring的ApplicationContext
之中的,它提供了一些方法来关联Spring的声明周期来进行回调,在下面的例子中,缓存会在构造后和销毁前进行回调。
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}