Spring IoC的依赖注入过程,也就是从容器中找到依赖对象、注入到当前对象的过程,叫装配。
我们前面看到的通过xml文件的
这种方式其实也比较简单实用 ---在类比较少、依赖关系比较简单的情况下。如果类比较多、依赖关系比较复杂的话,配置起来就很烦很啰嗦。
为了解决这个问题,Spring提供了自动装配功能。自动装配的意思就是,Spring根据某种装配规则自动完成装配,不再需要我们手工配置了。
自动装配能够避免臃肿的配置文件,提高开发效率。
自动装配使用起来也非常方便。还是使用我们前面的例子:
不需要为DependencyB设置指向dependencyA的ref,增加autowire=byname的配置,Spring自动就可以完成装配。
使用xml的启动类:
public class AppByXML {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("mySpring.xml");
System.out.println("Now start to getbean...");
DependencyB dependencyB = applicationContext.getBean(DependencyB.class);
dependencyB.testB();
}
}
执行启动类:
here you call set method...
Now start to getbean...
This is DependencyB's test and ...I am DependencyA test...
配置文件中并没有指定ref,Spring可以自动按照规则完成装配。
自动装配规则
Spring提供如下4种自动装配规则:
- no: 不执行自动装配。这个时候只能手动装配了。
- byName: 根据属性名完成自动装配。Spring根据当前属性的名字,查找容器中id或name等于该名字的Bean进行装配。找不到则不装配。
- byType: 根据属性的类型完成装配。Spring Ioc容器中该类型的Bean多于一个的话,抛异常。
- constructor: 类似byType,只不过是用在构造方法参数中,为构造方法的参数进行装配。
上例中我们如果把装配规则修改为byType:
执行启动类:
here you call set method...
Now start to getbean...
This is DependencyB's test and ...I am DependencyA test...
自动装配成功。
但是如果我们修改配置,再加入一个dependencyA之后:
再次执行启动类:
抛出异常UnsatisfiedDependencyException...
expected single matching bean but found 2: dependencyA,dependencyA1
所以byType的情况下,Spring IoC容器中只能有一个该类型的Bean存在,多于一个则抛异常。
自动装配的好处和弊端
自动装配的好处就是可以简化配置,瘦身臃肿的配置文件,在设计变化后,比如增加或减少依赖关系的时候不需要执行繁琐的配置文件同步修改工作,极大提高开发人员的效率。
但是自动装配也还是存在一些弊端或使用限制,包括:自动装配优先级低于手动装配,也不能对基础数据类型、Strings、Arrays等执行自动装配。以byType进行装配的时候,如果容器中存在多个该类型的Bean则会抛出异常等等。
但是这些弊端和限制其实是可以有效避免的,基础数据类型只能通过手动的方式进行装配,而且实际项目开发过程中需要装配基础数据类型的场景应该非常少。对于同一个类型存在多个Bean的情况,我们其实在实际应用中应该尽可能通过设置primary或qulifier指定在装配过程中优先装配哪一个Bean,比如上面那个例子:
指定primary之后就没有问题了,最终dependencyA1会装配到DependencyB中。
所有项目中其实还是优先使用自动装配,因为好处明显多于弊端。
通过注解实现自动装配
@Autowired注解实现自动装配,可以作用在属性、方法、构造器上。
作用在属性上实现对该属性的自动装配,作用在构造器上、方法上,可自动装配构造器或方法参数。
@Autowired自动装配规则:(源码位置DefaultListableBeanFactory#doResolveDependency)
首先按类型(byType)匹配,匹配过程中会处理@Quarlifier:检查有@Quarlifier设置并且有匹配的Bean则返回,否则如果设置了@Quarlifier但是没有匹配的Bean则抛异常。
匹配后有以下几种情况:
- 没匹配到:@Autowired注解的参数required设置为true(默认为true)则抛异常,否则返回null
- 只匹配到一个Bean:直接注入
匹配到多个:需要进一步判断:
优先检查@Primary的Bean。没有@Primary则检查优先级的设置,返回最高优先级的Bean。
最后检查如果有与当前属性名称匹配的Bean则返回,没有的话就表明无法成功注入,根据required的设置抛异常(如果设置为true)或者返回null(如果设置为true)。
@Qulifier注解针对属性设置:
@Component
public class DependencyB implements IDependencyB{
@Autowired
@Qualifier(value="dependencyA")
private IDependencyA d;
@Primary注解则针对Bean设置,同类型的Bean只有一个可以设置为@Primary,否则在DI过程中会报错:
@Service
@Primary
public class DependencyA implements IDependencyA {
@Override
public void test(){
System.out.println("I am DependencyA test...");
}
}