Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式

前言

IoC是Spring的核心思想之一,常用的实现IoC的技术就是依赖注入


Spring 依赖注入简单实现

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第1张图片

Beer:

package pojo;

public class Beer {
    public void toDrink(){
        System.out.println("喝啤酒");
    }
}

Person(注入Beer对象):

package pojo;

public class Person {

    private Beer beer;

    public void setBeer(Beer beer) {
        this.beer = beer;
    }

    public void drink(){
        beer.toDrink();
    }
}

application.xml(Spring配置文件):

<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
    https://www.springframework.org/schema/beans/spring-beans.xsd">
	
    <bean id="person" class="pojo.Person">
        <property name="beer" ref="beer"/>
    </bean>
    <bean id="beer" class="pojo.Beer"/>
</beans>

MyTest:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.Person;

public class MyTest {
    @Test
    public void test(){
        //ClassPathXmlApplicationContext解析配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
        Person person = (Person) applicationContext.getBean("person");
        person.drink();
    }
}

运行:
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第2张图片

这就是一个简单的Spring依赖注入的案例

依赖注入:由容器将对象注入到需要的地方,我们仅需要被动接受即可(案例中将Beer对象注入到Person类中)
这是常用的IoC的实现方法


Spring中的依赖注入方式

Spring官网:Core详细介绍了依赖注入

其中,有3种配置方式
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第3张图片

  • XML Schema-based configuration:基于XML模式的配置,最基础的通过xml配置bean
  • Annotation-based Container Configuration:基于注解的配置
  • Java-based Container Configuration:通过Java程序配置(完全摆脱XML文件,是SpringBoot的核心之一)

支持两种注入方式:
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第4张图片

  • Constructor-based Dependency Injection:构造器注入
  • Setter-based Dependency Injection:基于Setter方法注入

后续一一解析


XML Schema-based configuration

基于XML文件的依赖注入是最基础的注入方式

我们上面的案例就是基于XML文件的注入

需要了解Spring对应的XML配置文件:

<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
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="pojo.Person">
        <property name="beer" ref="beer"/>
    </bean>
    <bean id="beer" class="pojo.Beer"/>
</beans>
  • 约束:spirng采用的是Schema约束,约束文件是xsd关于XML约束
    schema约束比dtd约束更强,可以约束文本内容

我们点入该xsd约束的bean标签(ctrl+鼠标左键点击标签)
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第5张图片

约束了很多东西:
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第6张图片

总之,bean标签表示的就是我们的对象

通过将id=beer的bean注入到了person中

property标签中,name对应的是Person类的成员变量名,ref表示引用其他bean
如果只是一个简单属性可以通过Value值
这里的property标签就3个属性

这是一种setter注入

关于Spring.xml配置文件,有很多值得注意的细节,后续深入


Setter注入和构造器注入

通过阅读官网,知道
构造器注入:
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第7张图片

通过constructor-arg子标签构造器注入

Setter注入:
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第8张图片

通过property子标签Setter注入

我们可以通过一个案例简单查看:

修改前面的Person类:

package pojo;


public class Person {

    private Beer beer;

    public Person() {
        
    }

    public Person(Beer beer) {
        this.beer = beer;
        System.out.println("有参构造方法");
    }

    public void setBeer(Beer beer) {
        this.beer = beer;
        System.out.println("Setter注入");
    }

    public void drink(){
        beer.toDrink();
    }
}

再运行

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第9张图片

确实是Setter方法注入

然后修改一下xml中的配置:

    <bean id="person" class="pojo.Person">
        <constructor-arg name="beer" ref="beer"/>
    </bean>
    <bean id="beer" class="pojo.Beer"/>

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第10张图片

ok,验证了上面的结论正确

  • property标签是Setter方法注入
  • constructor-arg标签是有参构造方法注入

XML注入的自动装配

从上面的案例的XML文件的配置,可以清晰的看出两个Bean之间的关系(不管是property还是constructior-arg
表现了父子的依赖关系,显然,这很麻烦,类结构表现了依赖关系,xml中又要表现一次,而且当新增属性,又要多加子标签

XML提供了全局自动装配和局部自动装配,自动装配就是Spring自己根据类结构将表现bean的依赖关系

  • 全局自动装配:XML根标签末尾加default-autowire=""配置

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第11张图片

这样,整个beans标签内的bean都会自动装配

  • 局部自动装配:在对应的父bean标签添加属性autowire=""

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第12张图片

装配属性:
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第13张图片

  • no:默认值,不开启自动装配

  • default:默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的

  • byName:Spring容器查看XML配置文件中autowire属性设置为byName的bean(全局或局部)。然后,它尝试将其属性与配置文件中相同名称定义的bean进行匹配并连接。如果找到匹配,它将注入这些bean。否则,bean将不会被注入

如上面案例中,byName可以注入,但当修改属性名就无法注入
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第14张图片
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第15张图片

原因是属性名不对,Setter方法名不对public void setBee(Beer bee),自然无法注入,可以通过在成员变量上加上标签@Qualifier自定义别名解决

  • byType:通过参数的数据类型自动装配,如果一个bean的数据类型和另外一个bean的property属性的数据类型兼容,就自动装配
    byType主要涉及到继承关系

Car继承体系:

package pojo.bytype;

interface Car {
}

class FirstCar implements Car{}

class SecondCar implements Car{}

Person类:

package pojo.bytype;

public class Person {
    //自动注入,但是以byType方法,变量名需要对齐类名
    private Car car;

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Person{" +
                "car=" + car +
                '}';
    }
}

在xml配置文件中:

<bean id="person" class="pojo.bytype.Person" autowire="byType"/>

<bean id="firstCar" class="pojo.bytype.FirstCar"/>

这样就可以运行成功,使用byType,不用关心实体类成员变量name与bean的id
但如果有两个同类型的bean就无法识别

<bean id="person" class="pojo.bytype.Person" autowire="byType"/>

<bean id="firstCar" class="pojo.bytype.FirstCar"/>
<bean id="secondCar" class="pojo.bytype.SecondCar"/>

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第16张图片

因为我们在person类中的属性是Car car,有两个指向car类型的bean,无法识别

  • constructor:通过构造器自动装配
    很明显,是通过有参构造方法自动装配的

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第17张图片

小结:

  • 全局自动装配是在标签添加属性default-autowire=""
  • 局部自动装配是在想要注入的标签添加属性autowire
  • 自动装配有5个类型,no是spring默认的,无自动装配,default是采用上一节标签的自动装配方案
  • byName是通过bean的id与要注入的实体类属性名对应,通过Setter注入
  • byType是通过bean类型与要注入的实体类属性类型对应,所以一个注入的类型只能有一个bean,通过Setter注入
  • constructor通过有参构造器注入

Annotation-based Container Configuration

基于注解的依赖注入,Spring提供了注解的方式简便XML配置

我们前面需要指定特定的实体类,而通过在实体类上注解,Spring自动扫描装配即可代替

  1. 标记实体类
    Spring提供了一些标签:@Component、@Service、@Controller等等,最基础的是@Component,其他的标记注解都是基于这个基础注解,这个注解表示类是Spring的组件bean,即会被Spring扫描成bean
package pojo.bytype;

import org.springframework.stereotype.Component;

interface Car {
}
@Component
class FirstCar implements Car{}

class SecondCar implements Car{}

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第18张图片

  1. 注入属性@Autowired,类似与前面的自动装配,这个是自动注入
    当然还有@Resource(JSR-250定义),@Inject(JSR-330定义)
    属性名、构造方法、Setter方法都可以添加该注解,即可以切换注入方式(添加在属性名、构造方法上就是有参构造器注入,添加在Setter方法上就是Setter注入

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第19张图片

  1. 告诉Spring需要自动扫描
    即然实体类都标记好了,就需要告诉Spring开启扫描
<context:component-scan base-package="pojo.bytype"/>

从官方的注释看:
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第20张图片

的使用隐式启用的功能。使用时,通常无需包含元素。

在使用注解时,需要在Spring中配置AnnotationBeanPostProcessor,如autowired注解需要,当然,这是一种完整的做法

是隐式地向Spring容器注册AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor以及PersistenceAnnotationBeanPostProcessor这4个BeanPostProcessor,也就是可以直接使用注解

运行:

    @Test
    public void test3(){
        //ClassPathXmlApplicationContext解析配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("annotation.xml");
        pojo.bytype.Person person = (pojo.bytype.Person) applicationContext.getBean("person");
        System.out.println(person.toString());
    }

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第21张图片

那么是使用什么自动装配方式呢?byName、byType、constructor?

修改一下Car的名字private Car myCar;

测试:
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第22张图片

很明显,不是byName

在两个Car子类上都添加@Component
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第23张图片

再运行:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'pojo.bytype.Car' available: expected single matching bean but found 2: firstCar,secondCar

报错,提醒我们有两个Car类
在这里插入图片描述

明显,就是byType自动注入方式,但当我们把属性名该为对应的注入类的类名
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第24张图片
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第25张图片

可以注入,注入的确实是FirstCar

当byType失败时,会尝试byName,都失败就报错

如果不想改属性名,Spring还提供了别名注解@Qualifier("firstCar")
Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第26张图片


Java-based Container Configuration

基于Java类的依赖注入

上面还是需要在XML配置文件中配置标签,可不可以完全抛弃XML文件呢?

基于Java类+注解可以实现(SpringBoot就是这种方式)

需要两个注解:

  • @Configuration:表示这个Java类是Spring配置类,替代XML
  • @ComponentScan(value = "pojo.bytype")表示该配置类扫描哪个包,替代XML中的

基于Java的依赖注入步骤:

  • 设置一个配置类:
package pojo.bytype;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(value = "pojo.bytype")
public class Config {
}
  • 修改测试类:由于我们已经没有了XML配置文件,就不能使用ClassPathXmlApplicationContext类解析了,Spring提供了AnnotationConfigApplicationContext解析注解配置文件
@Test
public void test4(){
    ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
    pojo.bytype.Person person = (pojo.bytype.Person) context.getBean("person");
    System.out.println(person.toString());
}

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第27张图片

这样,就完全不需要XML配置文件了

当然,如果不使用自动扫描,也可以在JavaConfig里配置Bean

package pojo.bytype;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config {

    @Bean
    public Car car(){
        return new FirstCar();
    }

    @Bean
    public Person person(){
        return new Person();
    }

}

Spring - 依赖注入(DI)解析,三种配置方式、两种注入方式_第28张图片

一样可以注入


总结

  • 依赖注入有3种模式:基于XML配置文件、基于注解,基于Java配置类(这三种可以混搭),有2种注入方式:Setter和有参构造
  • 基于XML配置文件的依赖注入比较麻烦,需要在XML配置文件设置bean及依赖关系,可以通过自动装配简化
  • 全局自动装配default-autowire="",局部自动装配autowire=""
  • 基于注解,可以在需要Spring容器获得的类上添加@Component注解,需要注入的地方添加@Autowire(属性名、构造方法、Setter方法都可以),在XML配置文件中只需要标记需要扫描的包
  • 基于Java配置类@Configuration表示该Java类为配置类,代替XML @ComponentScan(value = "pojo.bytype")表示扫描的包,测试类需要AnnotationConfigApplicationContext类解析注解配置类

学海无涯苦作舟

都看到这了,点个赞呗(^_−)☆

你可能感兴趣的:(Spring,spring,java,xml)