前言:
在看下面的内容之前,我们要对自动注入和精确注入有一个大概的了解,所谓精确注入就是指我们通过构造函数或者setter方法指定了我们对象之间的依赖,也就是上篇讲的依赖注入然后spring根据我们指定的依赖关系,精确的给我们完成了注入,那么自动注入是什么?我们看一下下面一段代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<bean id="auto" class="com.phr.service.AutoService" autowire="byType"/>
<bean id="phrService" class="com.phr.service.YxService"/>
</beans>
public class AutoService {
YxService yxService;
public void setYxService(YxService yxService) {
System.out.println("注入yxService" + yxService);
this.yxService = yxService;
}
}
public class YxService {
}
public class Main03 {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("spring.xml");
System.out.println(cc.getBean("auto"));
}
}
在上面的例子中我们可以看到
1、我们没有采用注解@Autowired进行注入
2、XML中没有指定属性标签
3、没有使用构造函数
但是打印结果如下
注入yxServicecom.phr.service.YxService@45ff54e6
com.phr.service.AutoService@4629104a
可能细心的同学已经发现了端倪,在AutoService的标签中加了一个属性autowire=“byType”,那么这个属性是什么意思呢?为什么加了这个属性就能帮助我们自动注入呢?不要急,我们带着问题往下看
自动注入:
这一部分内容主要涉及官网的1.4.5小结
我们先看官网怎么说的?
自动注入的优点
Spring容器可以自动装配协作bean之间的关系。您可以通过检查ApplicationContext的内容,让Spring为您的bean自动解析协作者(其他bean)。自动装配有以下优点:自动装配可以显著减少指定属性或构造函数参数的需要。(本章其他地方讨论的其他机制,比如bean模板,在这方面也很有价值。)自动装配可以随着对象的发展而更新配置。例如,如果您需要向一个类添加一个依赖项,那么该依赖项可以自动满足,而不需要修改配置。因此,自动装配在开发过程中特别有用,而不需要在代码库变得更加稳定时放弃切换到显式连接的选项。
注入模型:
接下来官网给我们介绍了自动注入的四种模型,如图
我们一一进行解释并测试:
no
这是spring目前默认的注入模型,也就是说默认情况下spring是关闭自动注入,必须要通过setter方法或者构造函数完成依赖注入,并且spring也不推荐修改配置
还是刚才那个例子,我们把autowire="byType"这个去掉测试一下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<bean id="auto" class="com.phr.service.AutoService"/>
<bean id="phrService" class="com.phr.service.YxService"/>
</beans>
public class Main04 {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("spring.xml");
ConfigurableListableBeanFactory beanFactory = cc.getBeanFactory();
GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("auto");
System.out.println(genericBeanDefinition.getAutowireMode());
}
}
输出结果为:
0
说明spring默认是no
bytype
测试代码跟之前唯一不同的就是修改配置autowire="byType"
,这里我们测试以下三种异常情况
1、找不到合适类型的bean,发现不报异常,同时不进行注入
2、找到多个合适类型的bean,spring会直接报异常(No qualifying bean of type ‘com.phr.service.YxService’ available: expected single matching bean but found 2: yxService,phrService)
3、set方法中有两个参数,且两个参数都能找到唯一类型符合的bean,不报异常,也不进行注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<bean id="auto" class="com.phr.service.AutoService" autowire="byType"/>
<bean id="phrService" class="com.phr.service.YxService"/>
</beans>
public class Main04 {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("spring.xml");
ConfigurableListableBeanFactory beanFactory = cc.getBeanFactory();
GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("auto");
System.out.println(genericBeanDefinition.getAutowireMode());
}
}
输出结果为
注入yxServicecom.phr.service.YxService@45ff54e6
2
另外需要说明的是,我在测试的时候set方法这样命名,public void set(YxService yxService) {也不会进行自动注入。
byName
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<bean id="auto" class="com.phr.service.AutoService" autowire="byName"/>
<bean id="phrService" class="com.phr.service.YxService"/>
</beans>
输出结果为
注入yxServicecom.phr.service.YxService@5a2e4553
1
这种方式,我们完成spring自动注入需要两个条件:
1、提供setter方法
2、如果需要注入的属性是xxx,那么setter的方法名必须是setxxx,就是说命名必须规范;
在找不到对应名称为Bean的情况下,spring也不会报错,只是不会完成给我们注入,测试例子如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<bean id="auto" class="com.phr.service.AutoService" autowire="byType"/>
<bean id="phrService" class="com.phr.service.YxService"/>
</beans>
public class Main04 {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("spring.xml");
ConfigurableListableBeanFactory beanFactory = cc.getBeanFactory();
GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("auto");
System.out.println(genericBeanDefinition.getAutowireMode());
}
}
输出结果为:
1
另外我在测试的时候发现,这种情况下,如果我们提供的参数不规范也不会完成注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<bean id="auto" class="com.phr.service.AutoService" autowire="byName"/>
<bean id="yxService" class="com.phr.service.YxService"/>
<bean id="indexService" class="com.phr.service.IndexService"/>
</beans>
public class AutoService {
YxService yxService;
IndexService indexService;
public void setYxService(YxService yxService,IndexService indexService) {
System.out.println("注入yxService" + yxService);
this.yxService = yxService;
}
}
public class IndexService {
}
public class Main04 {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("spring.xml");
ConfigurableListableBeanFactory beanFactory = cc.getBeanFactory();
GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("auto");
System.out.println(genericBeanDefinition.getAutowireMode());
}
}
输出结果为
1
本以为这种情况Spring会注入yxService,indexService为null,实际测试过程中发现这个set方法根本不会被调用,说明Spring在选择方法时,还对参数进行了校验,byName
这种注入模型下,参数只能是我们待注入的类型且只能有一个
constructor
当我们使用这种注入模型时,spring会根据构造函数去查找有没有对应名称的bean有的话完成注入,如果根据名称没有找到,那么它会根据类型查找,如果根据类型还是没有找到,就会报错。
自动注入的缺陷:
这里不得不说一句,spring在这一章节中有三分之二的内容在说自动注入的缺陷以及如何将一个类从自动注入中移除,结合情景考虑自动注入默认是关闭的,不过这个也可以自己修改。可以说明,在实际使用情况中,Spring是非常不推荐我们开启自动注入这种模型的。从官网中我们总结自动注入有以下几个缺陷:
1、精确注入会覆盖自动注入。并且我们不能注入基本数据类型,字符串,Class类型(这些数据的数组也不行)。而且这是Spring故意这样设计的
2、自动注入不如精确注入准确。而且我们在使用自动注入时,对象之间的依赖关系不明确
3、对于一些为Spring容器生成文档的工具,无法获取依赖关系
4、容器中的多个bean定义可能会与自动注入的setter方法或构造函数参数指定的类型匹配。对于数组、集合或映射实例,这可能不会产生什么问题。但是,对于期望单个值的依赖项,我们无法随意确定到底有谁进行注入。如果没有唯一的bean定义可用,则会抛出异常
如何将bean从自动注入中移除
这里主要使用到autowire-candidate
这个属性,我们要将其设置为false
,这里需要注意以下几点
1、这个设置只对类型注入生效。这也很好理解,例如我们告诉Spring要自动注入一个indexService,同时我们又在indexService的配置中将其从自动注入中排除,这就是自相矛盾的。所以在byName的注入模型下,Spring直接忽略了autowire-candidate这个属性
2、autowire-candidate=false
这个属性代表的是,这个bean不作为候选bean注入到别的bean中,而不是说这个bean不能接受别的bean的注入。例如在我们上面的例子中我们对AutoService
进行了如下配置
<bean id="auto" class="com.phr.service.AutoService" autowire="byType" autowire-candidate="false"/>
代表的是这个bean不会被注入到别的bean中,但是yyService
任何会被注入到AutoService
中
另外需要说明的是,对于自动注入,一般我们直接在顶级的标签中进行全局设置,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
default-autowire="byType">
</beans>
自动注入和精确注入的比较总结
从关注的点上来看,自动注入是针对的整个对象,或者一整批对象。比如我们如果将autoService这个bean的注入模型设置为byName,Spring会为我们去寻找所有符合要求的名字(通过set方法)bean并注入到autoService中。而精确注入这种方式,是yy我们针对对象中的某个属性,比如我们在autoService中的yyService这个属性字段上添加了@AutoWired注解,代表我们要精确的注入yyService这个属性。而方法注入主要是基于方法对对象进行注入。
我们通常所说***byName***,byType***跟我们在前文提到的注入模型中的byName,byType是完全不一样的。通常我们说的***byName,***byType***是Spring寻找bean的手段。比如,当我们注入模型为constructor时,Spring会先通过名称找对符合要求的bean,这种通过名称寻找对应的bean的方式我们可以称为byName。我们可以将一次注入分为两个阶段,首先是寻找符合要求的bean,其次再是将符合要求的bean注入。也可以画图如下:
depends-on:
我们首先要知道,默认情况下,Spring在实例化容器中的对象时是按名称进行自然排序进行实例化的。比如我们现在有A,B,C三个对象,那么Spring在实例化时会按照A,B,C这样的顺序进行实例化。但是在某些情况下我们可能需要让B在A之前完成实例化,这个时候我们就需要使用depends-on这个属性了。我们可以通过形如下面的配置完成:
<bean id="a" class="xx.xx.A" depends-on="b"/>
<bean id="b" class="xx.xx.B" />
或者
@Component
@DependsOn("b")
public class A {
}
lazy:
默认情况下,Spring会在容器启动阶段完成所有bean的实例化,以及一系列的生命周期回调。某些情况下,我们可能需要让某一个bean延迟实例化。这种情况下,我们需要用到lazy
属性,有以下两种方式:
XML中bean标签的lazy-init
属性
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
或者
@Component
// 懒加载
@Lazy
public class A {
}
nent
@DependsOn(“b”)
public class A {
}
##### lazy:
默认情况下,Spring会在容器启动阶段完成所有bean的实例化,以及一系列的生命周期回调。某些情况下,我们可能需要让某一个bean延迟实例化。这种情况下,我们需要用到`lazy`属性,有以下两种方式:
XML中bean标签的`lazy-init`属性
```Java
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
或者
@Component
// 懒加载
@Lazy
public class A {
}
对此,我们已经系统的学习spring依赖相关的知识