Spring依赖注入方式以及对应的注入模型

前言

这几天小编在阅读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 modeExplanation
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官方的理解,其实除了自动注入外,其他都为手动注入,而且注入的方式有两种。

  1. 通过xml显示知道注入的类字段,需要加入构造或者setter函数
  2. 加入 @Autowired等注解

不知道小编根据官方文档理解是否正确,需要各路大神来指点一二。

总结

这篇文章可能代码比较长,希望各位可以看得明白,而且与网上的很多文章相悖。观点不符,也希望各位大神自己斟酌。谢谢。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木兮君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值