前言
这几天小编在阅读spring的官方文档,然后发现一个以前没有注意的问题,也就是依赖注入,小编写业务代码的时候总是很简单,一个类引用另一个类的时候直接加@Autowired或者@Resource注解,但是idea在使用@Autowired总是报黄线,而@Resource其实并不是spring的注解而是javax.annotation下的注解,spring可以识别。但是假如不加这些注释,spring是否可以自动注入呢,下面小编带大家看一下官方文档。
官方文档
spring官方文档: Dependency Injection.
其中小编节选比较重要的点,来给大家说明一下:
DI exists in two major variants: Constructor-based dependency injection and Setter-based dependency injection.
翻译过来就是:依赖注入有两种主要的变体:基于构造函数的依赖注入和基于setter方法的依赖注入。这两种大家可以自己选择。而当@Autowired放在构造函数或setter方法上的时候黄线也就取消了。当然使用@Autowired注解没有上述的两个方法spring依旧可以注入,他是通过反射的原理直接将值赋予进去即可。官方文档的话推荐大家使用构造函数的自动注入,但是当spring bean出现循环依赖的时候,使用setter方法是可以有效解决的。大家有空可以看一下文档,接下来小编会通过代码示例展开。
代码示例
首先创建A,B,D三个类,让A 被扫描到并注册到spring容器中,B和D并没有
@Component
public class A {
private B b;
private D d;
public A(){
}
public void setB(B b) {
this.b = b;
}
public A(D d) {
this.d = d;
}
public void printBandD() {
System.out.println("b=" + b + " and d=" + d);
}
}
public class B {
}
public class D {
}
测试类
@ComponentScan("com.dtyunxi.yundt.autodi")
public class DiTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(DiTest.class);
A bean = applicationContext.getBean(A.class);
bean.printBandD();
}
}
以及查看a的注入模型
@Component
public class MyBeanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
AbstractBeanDefinition a = (AbstractBeanDefinition)beanFactory.getBeanDefinition("a");
System.out.println(a.getAutowireMode());
}
}
运行结果
0
b=null and d=null
此时非常容易理解只有A类被注入到spring容器中,B和D类没有当然不行了。所有打印出来都为null,但是注入模型为0这是什么意思呢。下面咱们说一下注入模型。
注入模型
Autowiring mode | Explanation |
---|---|
AUTOWIRE_NO (值为0) | (默认)没有自动装配。Bean引用必须由ref元素定义。对于较大的部署,不建议更改默认设置,因为显式地指定协作者可以提供更大的控制和清晰度。在某种程度上,它记录了系统的结构。 |
AUTOWIRE_BY_NAME( 值为1) | 通过属性名自动装配。Spring寻找与需要自动连接的属性同名的bean。例如,如果一个bean定义按名称设置为自动装配,并且它包含一个master属性(也就是说,它有一个setMaster(…)方法),Spring就会查找一个名为master的bean定义,并使用它来设置属性。 |
AUTOWIRE_BY_TYPE( 值为2) | 如果容器中恰好存在该属性类型的一个bean,则自动连接该属性。如果存在多个,将抛出一个致命异常,这表明您不能对该bean使用byType自动装配。如果没有匹配的bean,则什么也不会发生(属性没有设置)。 |
AUTOWIRE_CONSTRUCTOR(值为3) | 类似于byType,但适用于构造函数参数。如果容器中没有确切的构造函数参数类型的bean,则会引发致命错误。 |
AUTOWIRE_AUTODETECT(值为4) | 通过bean的内省来选择合适的自动装配策略(根据英文翻译,就是spring会选择最适合装备的策略) |
以上注入模型是在AutowireCapableBeanFactory内部的常量。官方文档没有最后的自动检测,最后的自动检测是spring 3.0之后引入。
修改示例
咱们加入xml文件来让上面a中的b和d注入进来
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id = "a" class="com.dtyunxi.yundt.autodi.A">
<!-- constructor injection using the nested ref element -->
<constructor-arg name="d" ref="d"/>
<!-- setter injection using the nested ref element -->
<property name="b" ref="b"/>
</bean>
<bean id = "b" class="com.dtyunxi.yundt.autodi.B">
</bean>
<bean id = "d" class="com.dtyunxi.yundt.autodi.D"/>
</beans>
修改测试类
@ComponentScan("com.dtyunxi.yundt.autodi")
@ImportResource("spring.xml")
public class DiTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(DiTest.class);
A bean = applicationContext.getBean(A.class);
bean.printBandD();
}
}
打印结果
0
b=com.dtyunxi.yundt.autodi.B@2a32de6c and d=com.dtyunxi.yundt.autodi.D@7692d9cc
其实还是不是自动注入的方式,因为他还是默认值0,那咱们继续修改xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
//这边只能写一个。dafault和no其实都为0,byType为2,byName为1,constructor为3,不能配置自动检测
<bean id = "a" class="com.dtyunxi.yundt.autodi.A" autowire="default"/>
<bean id = "a" class="com.dtyunxi.yundt.autodi.A" autowire="no"/>
<bean id = "a" class="com.dtyunxi.yundt.autodi.A" autowire="byType"/>
<bean id = "a" class="com.dtyunxi.yundt.autodi.A" autowire="byName"/>
<bean id = "a" class="com.dtyunxi.yundt.autodi.A" autowire="constructor"/>
<bean id = "b" class="com.dtyunxi.yundt.autodi.B">
</bean>
<bean id = "d" class="com.dtyunxi.yundt.autodi.D"/>
</beans>
上面打印结果的话依次是
//default和no
0
b=null and d=null
//byType
2
b=com.dtyunxi.yundt.autodi.B@2a32de6c and d=null
//byName
1
b=com.dtyunxi.yundt.autodi.B@2a32de6c and d=null
//constructor
3
b=null and d=com.dtyunxi.yundt.autodi.D@2cbb3d47
上面自动注入的时候其实是需要setter或构造方法的,这是通过xml文件自动注入,那假如通过java代码呢。咱们接着往下看
B和D类我们同样交给spring关联,加入注解
@Component
public class B {
}
@Component
public class D {
}
然后修改一下MyBeanPostProcessor 类
@Component
public class MyBeanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
AbstractBeanDefinition a = (AbstractBeanDefinition)beanFactory.getBeanDefinition("a");
//修改一下autowireMode,用其中之一就行了
a.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
a.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
System.out.println(a.getAutowireMode());
}
}
将测试类中的@ImportResouce去掉即可
修改示例II
那为什么@Autowired就可以了呢
@Component
public class A {
@Autowired
private B b;
@Autowired
private D d;
public void printBandD() {
System.out.println("b=" + b + " and d=" + d);
}
}
@Component
public class MyBeanPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
AbstractBeanDefinition a = (AbstractBeanDefinition)beanFactory.getBeanDefinition("a");
System.out.println(a.getAutowireMode());
}
}
@ComponentScan("com.dtyunxi.yundt.autodi")
public class DiTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(DiTest.class);
A bean = applicationContext.getBean(A.class);
bean.printBandD();
}
}
打印结果
0
b=com.dtyunxi.yundt.autodi.B@525f1e4e and d=com.dtyunxi.yundt.autodi.D@75f9eccc
他同样不是自动注入,同样不需要setter和构造方法,上面小编说过其实通过反射来注入的。我们接下来再次来一个示例
//新加入一个C接口并有两个实现类
public interface C {
}
@Component
public class C1 implements C{
}
@Component
public class C2 implements C{
}
//修改B类
@Component
public class B {
@Autowired
private C c;
}
//测试类
@ComponentScan("com.dtyunxi.yundt.autodi")
//@ImportResource("spring.xml")
public class DiTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(DiTest.class);
B bean = applicationContext.getBean(B.class);
bean.printC();
}
}
打印报错
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b': Unsatisfied dependency expressed through field 'c'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.dtyunxi.yundt.autodi.C' available: expected single matching bean but found 2: c1,c2
但是假如将B类修改一下,那就没问题了。
//修改B类
@Component
public class B {
@Autowired
private C c1;
}
通过以上代码,@Autowired先根据类型判断然后根据名称判断。但却不是自动注入模型。那他和上面小编说的他是通过反射来做的,首先看类中的成员变量是否有Autowired注解,然后成员是类的话根据类型找,然后如果类型多个就报错了,如果要注入的成员名称也不同,那就继续找,找到了就注入,失败了就报错,而不像自动注入一样不报错仅仅注入的值为null。
结论
依据小编对spring官方的理解,其实除了自动注入外,其他都为手动注入,而且注入的方式有两种。
- 通过xml显示知道注入的类字段,需要加入构造或者setter函数
- 加入 @Autowired等注解
不知道小编根据官方文档理解是否正确,需要各路大神来指点一二。
总结
这篇文章可能代码比较长,希望各位可以看得明白,而且与网上的很多文章相悖。观点不符,也希望各位大神自己斟酌。谢谢。