IOC ,全称 (Inverse Of Control) ,中文意思为:控制反转, Spring 框架的核心基于控制反转原理。

什么是控制反转?
控制反转是一种将组件依赖关系的创建和管理置于程序外部的技术。
由容器控制程序之间的关系,而不是由代码直接控制
由于控制权由代码转向了容器,所以称为反转

对象与对象之间的关系可以简单的理解为对象之间的依赖关系:
依赖关系:在 A 类需要类 B 的一个实例来进行某些操作,比如在类 A 的方法中需要调用类 B 的方法来完成功能,叫做 A 类依赖于 B 类。

一个需要特定的依赖的组件一般会涉及一个依赖对象,在 IOC 的概念中叫做目标 (target) 。换句话说, IOC 提供了这样的服务,使一个组件能够在它的整个生命周期中访问它的依赖和服务,用这种方法与它的依赖进行交互。总的来说, IOC 能够被分解为两种子类型:依赖注入和依赖查找。
IoC 在应用开发中是一个非常有力的概念。如 Martin Flower 所述, IoC 的一种表现形式就是依赖性注射。依赖性注射用的是好莱坞原则, ” 不要找我,我会找你的。 ” 。换句来说,你的类不会去查找或是实例化它们所依赖的类。控制恰好是反过来的,某种容器会设置这种依赖关系。使用 IoC 常常使代码更加简洁,并且为相互依赖的类提供一种很好的方法。

Spring 容器

在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前, 必须对它进行实例化. 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用.

Spring 提供了两种类型的 IOC 容器实现.

BeanFactory: IOC 容器的基本实现.

ApplicationContext: 提供了更多的高级特性. 是 BeanFactory 的子接口.

BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;

ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合都直接使用 ApplicationContext 而非底层的 BeanFactory

springboot 入参编码注解_对象


在 Spring 的 IOC 容器里配置 Bean(在applicationContext.xml中配置)

id是Bean 的名称,这里我们对其细节归纳为以下三点:

  1. 在 IOC 容器中必须是唯一的
  2. 若 id 没有指定,Spring 自动将权限定性类名作为 Bean 的名字
  3. id 可以指定多个名字,名字之间可用逗号、分号、或空格分隔

注意:当applicationContext.xml中有多个相同类型的bean时,请不要使用class的这种getBean的方法,而是使用id来getBean。即利用类型返回IOC容器中的bean,但要求IOC容器中只有一个该类型的bean。
依赖注入的方式

Spring 支持 3 种依赖注入的方式

属性注入

构造器注入

工厂方法注入(很少使用,不推荐)

针对如下类讲解注入方式
建立一个Spring项目首先要记得通常引入这三个基础jar包
可以适当选择不同版本

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.3.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>4.3.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.7.RELEASE</version>
    </dependency>

属性注入

属性注入即通过 setter 方法注入Bean 的属性值或依赖的对象

属性注入使用 元素, 使用 name 属性指定 Bean 的属性名称,value 属性或 子节点指定属性值

属性注入是实际应用中最常用的注入方式

public class Car {
    public String brand;
    private String corp;    
    private double price;
    private int maxSpeed;


    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getCorp() {
        return corp;
    }
    public void setCorp(String corp) {
        this.corp = corp;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public int getMaxSpeed() {
        return maxSpeed;
    }
    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
    public String toString(){
        return "brand:"+brand+"corp"+this.corp+"/maxSpeed:"+maxSpeed+"/price:"+price;
    }


}
<!--     属性方式注入-->
    <bean id="car" class="com.lyf.ditype.Car">
        <property name="maxSpeed">
            <value>240</value>
        </property>
        <property name="brand">
            <value>红旗</value>
        </property>
        <property name="price">
            <value>200000</value>
        </property>
    </bean>

构造方法注入

通过构造方法注入Bean 的属性值或依赖的对象,它保证了 Bean 实例在实例化后就可以使用。

构造器注入在 元素里声明属性

我们现在先构造一个Car的bean类

public class Car {
    String name;
    double price;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public Car() {
        super();
    }
    public Car(String name1, double price1) {
        super();
        this.name = name1;
        this.price = price1;
    }
    @Override
    public String toString() {
        return "Car [name=" + name + ", price=" + price + "]";
    }
}

这里有三种方法,来实现构造器注入的bean声明:

<bean id="car_id" class="com.lyf.p.Car">
        <constructor-arg value="BM"></constructor-arg>
        <constructor-arg value="500000"></constructor-arg>
    </bean>

    <bean id="car_Index_id" class="com.lyf.p.Car">
        <constructor-arg value="400000" index="1"></constructor-arg>
        <constructor-arg value="BM" index="0"></constructor-arg>
    </bean>

    <bean id="car_name_id" class="com.lyf.p.Car">
        <constructor-arg name="price1" value="300000"></constructor-arg>
        <constructor-arg name="name1" value="BM"></constructor-arg>
    </bean>

第一种方式:利用构造器的参数顺序,依次排列各个construcctor-arg

第二种方式:用index属性指定构造器参数的顺序,从0开始

第三种方式:构造器参数的名字来指定是哪一个参数赋值

测试方法:

@Test
    public void test6_1()
    {
        //创建spring IOC容器对象。ClassPathXmlApplicationContext表示配置文件在类路径下,它是接口ApplicationContext的一个实现类。
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        //从IOC容器中获取Bean实例
        Car cBean=(Car)ctx.getBean("car_id");
        //调用对象方法
        System.out.println(cBean);
    }

    @Test
    public void test6_2()
    {
        //创建spring IOC容器对象。ClassPathXmlApplicationContext表示配置文件在类路径下,它是接口ApplicationContext的一个实现类。
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        //从IOC容器中获取Bean实例
        Car cBean=(Car)ctx.getBean("car_Index_id");
        //调用对象方法
        System.out.println(cBean);
    }

    @Test
    public void test6_3()
    {
        //创建spring IOC容器对象。ClassPathXmlApplicationContext表示配置文件在类路径下,它是接口ApplicationContext的一个实现类。
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        //从IOC容器中获取Bean实例
        Car cBean=(Car)ctx.getBean("car_name_id");
        //调用对象方法
        System.out.println(cBean);
    }

运行结果1:

Car [name=BM, price=500000.0]

运行结果2:

Car [name=BM, price=400000.0]

运行结果3:

Car [name=BM, price=300000.0]

注意:方式三的constructor-arg的name属性必须与bean的构造方法的形参名称一样,而不是和bean的属性名一样,需要特别注意!

构造方法的重载问题

好,现在我们还说明一个问题。请看这样一个例子。现在有一个bean, Car2的定义如下:

public class Car2 {
    String name;
    double price;
    int version;
    double speed;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public int getVersion() {
        return version;
    }
    public void setVersion(int version) {
        this.version = version;
    }
    public double getSpeed() {
        return speed;
    }
    public void setSpeed(double speed) {
        this.speed = speed;
    }
    public Car2() {
        super();
    }
    public Car2(String name, double price, int version) {
        super();
        this.name = name;
        this.price = price;
        this.version = version;
    }
    public Car2(String name, double price, double speed) {
        super();
        this.name = name;
        this.price = price;
        this.speed = speed;
    }
    public Car2(String name, double price, int version, double speed) {
        super();
        this.name = name;
        this.price = price;
        this.version = version;
        this.speed = speed;
    }
    @Override
    public String toString() {
        return "Car2 [name=" + name + ", price=" + price + ", version="
                + version + ", speed=" + speed + "]";
    }



}

我们的容器配制如下:applicationContext.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">

    <bean id="car2_id" class="com.lyf.beans.Car2">
        <constructor-arg value="BM" index="0"></constructor-arg>
        <constructor-arg value="500000" index="1"></constructor-arg>
        <constructor-arg value="1" index="2"></constructor-arg>
    </bean>

</beans>

现在我们尝试调用测试:使用赋予参数name、price、version的构造器:

@Test
    public void test7()
    {
        //创建spring IOC容器对象。ClassPathXmlApplicationContext表示配置文件在类路径下,它是接口ApplicationContext的一个实现类。
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        //从IOC容器中获取Bean实例
        Car2 c2Bean=(Car2)ctx.getBean("car2_id");
        //调用对象方法
        System.out.println(c2Bean);
    }

但是运行结果却是:

Car2 [name=BM, price=500000.0, version=0, speed=1.0]

getBean返回的使容器中由构造器public Car2(String name, double price, double speed)返回的对象。这就是构造器重载可能带来的问题。

解决方法是:

方法1:利用constructor-arg标签中的type属性,指定参数类型。

将刚才的容器配制改为:

<bean id="car2_id" class="com.lyf.beans.Car2">
        <constructor-arg value="BM" index="0"></constructor-arg>
        <constructor-arg value="500000" index="1"></constructor-arg>
        <constructor-arg value="1" index="2" type="int"></constructor-arg>
    </bean>

在运行test7测试方法:

Car2 [name=BM, price=500000.0, version=1, speed=0.0]

这样就OK了。

方法2:利用constructor-arg标签中的name属性,指定参数名称。

<bean id="car2_id" class="com.lyf.beans.Car2">
        <constructor-arg value="BM" index="0"></constructor-arg>
        <constructor-arg value="500000" index="1"></constructor-arg>
        <constructor-arg value="1" index="2" name="version"></constructor-arg>
    </bean>

在运行test7测试方法:

Car2 [name=BM, price=500000.0, version=1, speed=0.0]

大家记住spring的容器对应于applicationContext.xml配制文件,容器构造对象时在加载该配制文件的时候,而不是实际getBean的时候。