Spring框架的自动装配官网解析Autowiring Collaborators

前言

在SpringIOC的开发框架中,想要完成依赖注入需要把依赖关系描述清楚。第一个地方是要我们在类的定义,就是我们所写的代码里面体现出来依赖关系。第二个是在spring的配置中去描述我们定义好的依赖关系。如果按照这样两部操作,实际开发过程中就有了一个很麻烦的事情:维护依赖关系。比如一个类依赖了其他100个类,那就需要码农们手动在xml文件里面维护至少100个<bean>标签,而且不能出错,可以说非常的麻烦和繁琐。所以Spring框架为了解决这个问题就提出了自动装配这个概念。自动装配就是把维护的部分给省略去了,所以现在我们使用Spring框架的时候仅仅需要在类中提供依赖,继而把对象交给容器管理即可完成注入。可以说自动装配是Spring的精髓中的精髓,那么本篇我们就探究一下官方文档中是怎么介绍这个东西的。更多Spring内容进入【Spring解读系列目录】

Autowiring Collaborators

自动装配的Spring官方名字叫做Autowiring Collaborators。首先看下官方文档是怎么解释这个自动装配的,以下摘自官方文档:

The Spring container can autowire relationships between collaborating beans. You can let Spring resolve collaborators (other beans) automatically for your bean by inspecting the contents of the ApplicationContext.

这段话的大意就是:Spring容器可以自动装配合作的bean的关系。通过检查ApplicationContext这个东西,使得你能够让spring自动的为你的bean去解析合作者(也就是其他的bean)。

官方文档说的很绕,一句话总结就是:自动装配可以通过检查ApplicationContext,然后自动帮你维护你自己的bean之间的关系。

自动装配优点

有两大块,以下摘自官方文档:

Autowiring can significantly reduce the need to specify properties or constructor arguments. (Other mechanisms such as a bean template discussed elsewhere in this chapter are also valuable in this regard.)

大意:自动装配可以显著的减少属性配置和构造方法

Autowiring can update a configuration as your objects evolve. For example, if you need to add a dependency to a class, that dependency can be satisfied automatically without you needing to modify the configuration. Thus autowiring can be especially useful during development, without negating the option of switching to explicit wiring when the code base becomes more stable.

大意:当对象变更的时候,自动装配可以自动更新配置。比如添加依赖,如果需要给一个类添加一个依赖,这个过程使用自动装配可以自动进行,而不需要修改配置。所以官方说Autowiring在开发周期中非常有用,因为我们修改依赖也不需要显式的写出来,所以可以使代码更稳定。

自动装配的模式

官方文档也有写,一共有四种。本篇博文将会用byName和byType两种来解析Spring自动装配的操作。

ModeExplanation
no(Default) No autowiring. 不使用自动装配,配置no和default是一样的
byName通过指定Name来装配,其实这个也是通过setter方法来装配, 因为这个Name就是来自于setter方法。
byType通过指定Type(类型)来装配
constructor通过使用构造方法装配

但是官方文档上并没有详细的给出怎么装配的,所以,我们还是写几个例子。

全局指定
byType

第一种实现就是全局指定,为了更好的解释,我们使用xml来作为一个例子: Resource文件夹下新建一个spring-config.xml的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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="dao" class="com.example.demo.DemoDaoImpl"></bean>
    <bean id="service" class="com.example.demo.DemoService"></bean>
</beans>

举个简单的例子:我们现在有一个接口DemoDao,一个实现类DemoDaoImpl,一个业务类DemoService。

public interface DemoDao {  //接口
    void test();
}
public class DemoDaoImpl implements DemoDao{  //实现类
    @Override
    public void test() {
        System.out.println("test");
    }
}
public c
public class DemoService {  //业务类
    private DemoDao dao;  //依赖
    public void doTest(){
        dao.test();
    }
    public void setDao(DemoDao dao) { //setter方法
        this.dao = dao;
    }
}

然后我们还要有一个测试类DemoTest和一个配置类Spring。说明一下:尽管笔者这里要讲解的是基于xml配置的,但是由于基于java的配置比较好编码,这里笔者用的混合型的配置,如果不明白请移步【Spring框架容器开启注解的方式】,可以了解更多有关配置的内容。

@Configuration
@ComponentScan("com.example")
@ImportResource("classpath:spring.xml")
public class Spring {  //Spring配置类
}
public c
public class DemoTest {  //测试类
    public static void main(String[] args) {
        AnnotationConfigApplicationContext acac=new AnnotationConfigApplicationContext(Spring.class);
        DemoService service= (DemoService) acac.getBean("service");
        service.doTest();
    }
}

可以看出DemoService依赖了DemoDao,我们在代码里提供了一个依赖关系名字叫dao。而且还有一个setter方法作为未来注入的地方。如果按照这个例子不进行自动注入的话,那么我们就要在<bean></bean>里再维护一个依赖,用来描述DemoService和DemoDao的关系。我都已经在程序里描述清楚了,为什么我还要再给你配置一遍呢?开发起来就会很麻烦,如果依赖成千上百,那有的是时间去配置维护了。自动装配就是解决这个问题的。

如果要添加全局指定的自动装配的配置,只需要添加一个属性default-autowire="byType",修改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"
        default-autowire="byType"> <!-- byName | no | default | constructor-->
    <bean id="dao" class="com.example.demo.DemoDaoImpl"> </bean>
    <bean id="service" class="com.example.demo.DemoService"></bean>
</beans>

直接运行就可以打出test,这里我们使用的是" byType"方式。如果我们把default-autowire=" byType"这句话删掉,就会报一个空指针异常,这是为什么呢?那就要讲到自动装配的原理了。

Exception in thread "main" java.lang.NullPointerException
	at com.example.demo.DemoService.doTest(DemoService.java:12)
	at com.example.demo.DemoTest.main(DemoTest.java:13)
自动装配的原理

我们先解释什么是byType注入。比如程序扫描到了service这个id,发现它对应的类DemoService有一个依赖,而且发现这个依赖的类型(Type)是DemoDao,然后就会再去循环Spring整个容器当中去检索有没有一个类的接口或者和父类的类型是等于DemoDao,如果发现了就把这对象new出来然后赋给dao。笔者的例子中DemoDaoImpl就是实现的DemoDao,所以Spring就new了DemoDaoImpl给了它。到这里就很明白了是吧,如果我们删了default-autowire这个配置,那么Spring就不会new一个对象给dao。自然遇到dao.test();这行代码的时候,肯定会报空指针异常的。

冲突

那么其实这里还有一个问题。如果说我的程序里面有两个DemoDao怎么办?比如我再创建一个类DemoDaoImpl2也实现DemoDao。

public class DemoDaoImpl2 implements DemoDao{
    @Override
    public void test() {
        System.out.println("test 2");
    }
}

然后把DemoDaoImpl2也维护到配置文件中。其实如果用的是idea这里就已经报错了。

<?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"
        default-autowire="byType">
    <bean id="dao" class="com.example.demo.DemoDaoImpl"></bean>
    <bean id="dao1" class="com.example.demo.DemoDaoImpl2"></bean>
    <bean id="service" class="com.example.demo.DemoService"></bean>
</beans>

直接运行肯定报错。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'service' defined in class path resource [spring.xml]: Unsatisfied dependency expressed through bean property 'dao'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.demo.DemoDao' available: expected single matching bean but found 2: dao,dao1
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1524)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1404)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
	at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)
	at com.example.demo.DemoTest.main(DemoTest.java:8)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.demo.DemoDao' available: expected single matching bean but found 2: dao,dao1
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1285)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1509)
	... 12 more

大家看报的错误是expected single matching bean but found 2,Spring只要一个但是匹配了俩,冲突了。这个要怎么解决呢?其实我们可以使用byName的方式搞定

ByName

其实我们的<bean>里面还有一个name属性,如果说name没有指定,那么name默认和id是一样的。而这个name其实就是和里面的setter方法的名字有关系,注意并不是和声明的变量的名字有关。我们看DemoService里面的setter方法叫做setDao。Spring就会自动把set去掉然后首字母小写,变为dao。然后就去找dao这个id或者name。

<?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"
        default-autowire="byName">
    <bean id="dao" class="com.example.demo.DemoDaoImpl"></bean>
    <bean id="dao1" class="com.example.demo.DemoDaoImpl2"></bean>
    <bean id="service" class="com.example.demo.DemoService"></bean>
</beans>

如果说改成byName那么直接运行,就会打印test。那么如果我想打印test 2怎么改呢?如上所说,我们要改的就是DemoService里面的setter方法:

public class DemoService {
    private DemoDao dao;
    public void doTest(){
        dao.test();
    }
    public void setDao1(DemoDao dao) { //从setDao修改为setDao1,这样打印的就是test 2了
        this.dao = dao;
    }
}

如果大家还有疑问,或者我们解释的更彻底一些。我们把xml里的dao1改了成computer,然后我们修改setDao1为setComputer。再运行依然是test 2。
spring.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"
        default-autowire="byName">
    <bean id="dao" class="com.example.demo.DemoDaoImpl"> </bean>
    <bean id="computer" class="com.example.demo.DemoDaoImpl2"> </bean>
    <bean id="service" class="com.example.demo.DemoService"> </bean>
</beans>

DemoService:

public class DemoService {
    private DemoDao dao; //byName的扫描和声明的变量名字没有关系
    public void doTest(){
        dao.test();
    }
    public void setComputer(DemoDao dao) { //修改有关的地方,在setter方法上
        this.dao = dao;
    }
}

public class DemoService {
    private DemoDao computer; //修改变量名
    public void doTest(){
        dao.test();
    }
    public void setDao(DemoDao dao) { //这里不改,依然打印的是test,而不是test2
        this.dao = dao;
    }
}

这就是byName的作用。

单独指定

如果说我们配置了全局指定,但是发现了冲突,而又不免不了这个冲突必须让某一个类去使用怎么办呢?这时候就可以用单独指定的办法,修改配置文件spring.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"
        default-autowire="byType">
    <bean id="dao" class="com.example.demo.DemoDaoImpl"> </bean>
    <bean id="computer" class="com.example.demo.DemoDaoImpl2" > </bean>
    <bean id="service" class="com.example.demo.DemoService"  autowire="byName"> </bean>
</beans>

结语

那么这就是Spring的自动装配的原理和操作方法,也是Spring的最最核心的功能。但是我们并不常用这些内容,这里的讲解只是告诉大家他们的原理,那么下一篇【什么是@Autowired和@Reource以及其机制】就是真正和我们日常码代码有关的了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring自动装配Autowiring)是一种方便的依赖注入机制,它可以自动将bean之间的依赖关系进行解析和注入,而不需要显式地配置bean之间的关联。 在Spring中,有以下几种方式可以实现自动装配: 1. 根据类型自动装配Spring会根据bean的类型来自动匹配并注入依赖。使用`@Autowired`注解或者`<autowired>`标签进行标记。 ```java @Autowired private SomeDependency someDependency; ``` 2. 根据名称自动装配Spring会根据bean的名称来自动匹配并注入依赖。使用`@Autowired`注解结合`@Qualifier`注解或者`<qualifier>`标签进行标记。 ```java @Autowired @Qualifier("myDependency") private SomeDependency someDependency; ``` 3. 构造器自动装配Spring会根据构造器的参数类型和参数名称来自动匹配并注入依赖。使用`@Autowired`注解在构造器上标记。 ```java @Autowired public MyService(SomeDependency someDependency) { this.someDependency = someDependency; } ``` 4. 使用`@Resource`注解:`@Resource`注解是Java原生的注解,Spring也支持它进行自动装配。它可以根据bean的名称或者类型进行自动匹配。 ```java @Resource private SomeDependency someDependency; ``` 自动装配需要在Spring配置文件中启用。可以使用`<context:annotation-config>`标签或`@ComponentScan`注解来开启自动装配功能。 需要注意的是,自动装配是一个便利的功能,但在某些情况下可能会导致不可预料的问题,例如多个候选bean或循环依赖。因此,在使用自动装配时,建议仔细设计和管理bean之间的依赖关系,并在必要的时候明确指定装配方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值