8.2.6 自动装配和精确装配1 @Autowired注解
8.2.6 自动装配和精确装配
@Autowired注解
Spring提供了@Autowired注解来指定自动装配,@Autowired可以修饰setter方法、普通方法、实例变量和构造器等。
@Autowired修饰setter时
当使用@Autowired标注setter方法时,默认采用by Type自动装配策略。
例如下面的代码:
1 |
|
上面的代码中使用了@Autowired指定对setAxe()方法进行自动装配, Spring将会自动搜索容器中类型为Axe的Bean实例,并将该Bean实例作为setAxe()方法的参数传入。
- 如果正好在容器中找到一个类型为
Axe的Bean, Spring就会以该Bean为参数来执行setAxe()方法; - 如果在容器中找到多个类型为
Axe的Bean, Spring会引发异常; - 如果在容器中没有找到多个类型为
Axe的Bean, Spring什么都不执行,也不会引发异常
@Autowired修饰普通方法时
Spring还允许使用@Autowired来标注多个参数的普通方法,如下面的代码所示:
1 |
|
当使用@Autowired修饰带多个参数的普通方法时, Spring会自动到容器中寻找类型匹配的Bean,如果恰好为每个参数都找到一个类型匹配的Bean, 则Spring会自动以这些Bean作为参数来调用该方法。
以上面的prepare(Axe axe, Dog dog)方法为例, Spring会自动寻找容器中类型为Axe的Bean以及类型为Dog的Bean,如果在容器中恰好找到一个类型为Axe和一个类型为Dog的Bean, Spring就会以这两个Bean作为参数来调用prepare方法。
@Autowired也可用于修饰构造器和实例变量,如下面的代码所示:
1 |
|
@Autowired修饰实例变量时
当使用@Autowired修饰一个实例变量时, Spring将会把容器中与该实例变量类型匹配的Bean设置为该实例变量的值。
例如,程序中使用@Autowired标注了private Axe axe这个实例变量,则Spring会自动搜索容器中类型为Axe的Bean。
- 如果恰好找到一个该类型的
Bean, 那么Spring就会将该Bean设置成axe实例变量的值; - 如果容器中包含多于一个的
Axe实例,则Spring容器会抛出BeanCreateException异常。
@Autowired修饰构造时
@Autowired修饰构造时的注入规则与修饰普通方法的规则相同。
@Autowired修饰数组类型的成员变量时
@Autowired甚至可以用于修饰数组类型的成员变量,如下面的代码所示:
1 |
|
正如在上面的程序中看到的,被@Autowired修饰的axes实例变量的类型是Axe数组,在这种情况下, Spring会自动搜索容器中的所有Axe实例,并以这些Axe实例作为数组元素来创建数组,最后将该数组赋给上面Chinese实例的axes实例变量。
@Autowired修饰集合类型的实例变量时
与此类似的是,@Autowired也可标注集合类型的实例变量,或标注形参类型是集合的方法, Spring对这种集合属性、集合形参的处理与前面对数组类型的处理是完全相同的。例如如下代码:
1 |
|
@Autowired修饰集合类型的实例变量时 必须使用泛型
对于这种集合类型的参数而言,程序代码中必须使用泛型,正如上面程序中的代码所示,程序指定了该方法参数是Set<Axe>类型,这表明Spring会自动搜索容器中的所有Axe实例,并将这些实例注入到axes实例变量中。
如果程序没有使用泛型来指明集合元素的类型,则Spring不知道搜索那些类型的实例来注入集合。
@Autowired主机默认使用byType策略来自动装配
由于@Autowired默认使用byType策略来完成自动装配,系统可能出现有多个匹配类型的候选组件,此时就会导致异常。
@Autowired根据泛型进行自动装配
Spring 4.0增强后的@Autowired注解还可以根据泛型进行自动装配。例如,项目中定义了如下Dao组件(后文会介绍,Dao组件是Java EE应用中最重要的一类组件,用于执行数据库访问),本示例的基础Dao组件代码如下。
1 | package org.crazyit.app.dao.impl; |
BaseDaolmpl类中定义了所有Dao组件都应该实现的通用方法,而应用的其他Dao组件则只要继承BaseDaolmpl,并指定不同泛型参数即可。例如如下UserDaolmpl和ItemDaoImpl
1 |
|
1 |
|
接下来程序希望定义两个Service组件: UserServicelmpl和ItemServicelmp,而UserServicelmpl需要依赖于UserDaolmpl组件, ItemServicelmpl需要依赖于ItemDaolmpl组件,传统的做法可能需要为UserServicelmpl、 ItemServicelmpl分别定义成员变量,并配置依赖注入。
考虑到UserDaolmpl、 ItemServicelmpl依赖的都是BaseDaolmpl组件的子类,只是泛型参数不同而程序可以直接定义一个BaseServicelmpl,该组件依赖于BaseDaolmpl即可。例如如下代码:
1 | public class BaseServiceImpl<T> implements BaseService<T> |
上面程序中两行粗体字代码指定Spring应该寻找容器中类型为Basedao<T>的Bean,并将该Bean设置为dao实例变量的值。注意到BaseDao<T>类型中的泛型参数T, Spring不仅会根据Basedao类型进行搜索,还会严格匹配泛型参数T。
接下来程序只要定义如下UserServicelmpl即可
1 |
|
UserServiceImpl继承了BaseServiceImpl<User>,这就相当于指定了上面BaseDao<T>类型中T的类型为User,因此Spring会在容器中寻找类型为BaseDao<User>的Bean—此时会找到UserDaoImpl组件,从而实现将UserDaolmpl注入UserServiceImpl组件的功能。ItemServiceImpl的处理方法也与此类似,这样就可以很方便地将ItemDaolmpl注入ItemServiceImpl组件——而程序只要在UserServicelmpl、 ItemServiceImpl的基类中定义成员变量,并配置依赖注入即可,这就是Spring从4.0开始增强的自动装配。
该示例的主程序如下。
1 | public class BeanTest |
该主程序只是获取容器中的userService、 itemService两个Bean,并调用它们的方法。编译、运行该示例,可以看到如下输出:
1 | 调用org.crazyit.app.dao.impl.UserDaoImpl@133e16fd保存实体:org.crazyit.app.domain.User@51b279c9 |
从上面输出可以看出,@Autowired可以精确地利用泛型执行自动装配,这样即可实现将UserDaolmpl注入UserServiceImpl组件,将ItemDaolmpl注入Item Servicelmpl组件。