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
组件。