Spring IOC 理解

99 阅读4分钟
IoC(Inverse of Control:控制反转)

是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。

为什么叫控制反转?
  • 控制 :指的是对象创建(实例化、管理)的权力
  • 反转 :控制权交给外部环境(Spring 框架、IoC 容器)
IOC容器两方面理解
  • 抽象理解 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。

实现机制

  • Spring 中的 IOC 的实现原理就是工厂模式加反射机制。
  • 示例:
interface Fruit {
   public abstract void eat();
 }

class Apple implements Fruit {
    public void eat(){
        System.out.println("Apple");
    }
}

class Orange implements Fruit {
    public void eat(){
        System.out.println("Orange");
    }
}

class Factory {
    public static Fruit getInstance(String ClassName) {
        Fruit f=null;
        try {
            f=(Fruit)Class.forName(ClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}

class Client {
    public static void main(String[] a) {
        Fruit f=Factory.getInstance("io.github.dunwu.spring.Apple");
        if(f!=null){
            f.eat();
        }
    }
}
  • 具体理解 在 Spring 中,IoC 容器是 Spring 用来实现 IoC 的载体,IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
IOC容器作用

9880e70f5b6946068e8d64210196593e_tplv-k3u1fbpfcp-zoom-1.jpg 将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可。Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。

在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。

什么是Spring的依赖注入?

  • 控制反转IOC是一个很大的概念,可以用不同的方式来实现。其主要实现方式有两种:依赖注入和依赖查找依赖注入:相对于IOC而言,依赖注入(DI)更加准确地描述了IOC的设计理念。所谓依赖注入(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。
Spring IOC的五种注入方式
  • 基于xml配置:构造器注入、setter注入、静态工厂注入、实例工厂注入;
  • 基于注解配置:注解方式注入。

1. 构造器注入

  • 无参:spring默认是用无参构造器创建对象
public class Hello {
    private String name;
    public void show() {
        System.out.println("hello"+name);
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}

在Bean中直接指明属性名和值就可以了,注意标签是 property

<bean id="hello" class="com.ys.pojo.Hello">
    <property name="name" value="spring"/>
</bean>
  • 有参
public class Teacher {
    private String name;
    private int phone;
//    有参构造函数
    public Teacher(String name, int phone) {
        this.name = name;
        this.phone = phone;
    }
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getPhone() {
        return phone;
    }
 
    public void setPhone(int phone) {
        this.phone = phone;
    }
    
}

对应的Bean:(有参构造器其有三种方式注入:下标指明赋值,指明类型赋值,指明参数名赋值)

    <!--    有参构造下标赋值-->
    <bean id="teacher1" class="com.ys.pojo.Teacher">
        <constructor-arg index="0" value="李老师" />
        <constructor-arg index="1" value="333"/>
    </bean>
 
    <!--类型赋值法(不推荐这个)-->
    <bean id="teacher2" class="com.ys.pojo.Teacher">
        <constructor-arg type="java.lang.String" value="王老师"/>
        <constructor-arg type="int" value="34"/>
    </bean>
 
    <!--直接通过参数名设置-->
    <bean id="teacher3" class="com.ys.pojo.Teacher">
        <constructor-arg name="name" value="谢老师"/>
        <constructor-arg name="phone" value="5454545"/>
    </bean>

Set方式注入

set方式是依赖注入,依赖是指bean对象的创建依赖容器,注入是指bean对象中的所有属性,有容器来注入,如:

实体类Address

public class Address {
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Address{" +
                "name='" + name + '\'' +
                '}';
    }
 
}
public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String, String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
 
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address +
                ", books=" + Arrays.toString(books) +
                ", hobbys=" + hobbys +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Address getAddress() {
        return address;
    }
 
    public void setAddress(Address address) {
        this.address = address;
    }
 
    public String[] getBooks() {
        return books;
    }
 
    public void setBooks(String[] books) {
        this.books = books;
    }
 
    public List<String> getHobbys() {
        return hobbys;
    }
 
    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }
 
    public Map<String, String> getCard() {
        return card;
    }
 
    public void setCard(Map<String, String> card) {
        this.card = card;
    }
 
    public Set<String> getGames() {
        return games;
    }
 
    public void setGames(Set<String> games) {
        this.games = games;
    }
 
    public String getWife() {
        return wife;
    }
 
    public void setWife(String wife) {
        this.wife = wife;
    }
 
    public Properties getInfo() {
        return info;
    }
 
    public void setInfo(Properties info) {
        this.info = info;
    }
}

对应的bean

    <bean id="address" class="com.ys.pojo.Address">
        <property name="name" value="辽宁"/>
    </bean>
 
    <bean id="student1" class="com.ys.pojo.Student">
        <!--普通值注入,直接属性名+属性值-->
        <property name="name" value="小红"/>
        <!--Bean注入,ref+对应Bean的id-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>设计模式</value>
                <value>Java</value>
                <value>编程思想</value>
            </array>
        </property>
        <!--List注入-->
        <property name="hobbys">
            <list>
                <value>旅游</value>
                <value>学习</value>
            </list>
        </property>
        <!--Map注入-->
        <property name="card">
            <map>
                <entry key="QQ号" value="6666666"/>
                <entry key="手机号" value="8888888888"/>
            </map>
        </property>
        <!--Set注入-->
        <property name="games">
            <set>
                <value>飞车</value>
                <value>使命召唤</value>
            </set>
        </property>
        <!--NULL注入-->
        <property name="wife">
            <null/>
        </property>
        <!--Properties注入-->
        <property name="info">
            <props>
                <prop key="学号">121212</prop>
                <prop key="姓名">小明</prop>
            </props>
        </property>
    </bean>

测试:

    @Test
    public void Test1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = context.getBean("student1", Student.class);
        System.out.println(student);
    }

测试结果:

image.png

基于注解的注入

未完待续。。。