扫码关注微信公众号

回复“面试手册”,获取本站PDF版

回复“简历”,获取高质量简历模板

回复“加群”,加入程序员交流群

回复“电子书”,获取程序员类电子书

当前位置: Java > Spring高频面试题 > 6.什么是Spring IoC容器?

简单来说就是将创建对象的控制权交给Spring框架,IoC是控制反转的意思,这里的控制就是指创建对象的控制权,反转是指将本来在程序中创建对象的权力交给Spring。

如果是初学者,看到IoC这种概念性的描述不是很容易理解这玩意到底是干什么的,又有什么用?

IoC并不是Spring独有的,而是一种思想,IoC主要的优点有两个,降低代码之间的耦合度集中资源同一管理,简化开发

先举个比较容易理解的例子吧,先看不用IoC的写法,先确定一个简单的场景,学校、年级、班级(简单理解为学校由年级组成,年级由班级组成,班级由学生组成),大概需要三个Class,如下

class School{
    private Grade grade;
    School(){
        this.grade = new Grade();
    }

    public void printName(){
        System.out.println("路人大学");
    }

}

class Grade{
    private C c; //C表示班级
    Grade(){
        this.c = new C();
    }

}

class C{
    private int studentNum;
    C(){
        this.studentNum = 50;
    }
}

将代码运行起来,先new个大学,然后打印大学的名字

class test{
    public static void main(String[] args) {
        School school = new School();
        school.printName();
    }
}

输出

路人大学

这时,有人觉得班级的人数写死了,太不方便,要改成可动态修改的,这时需要修改三个类,如下

class School{
    private Grade grade;
    School(int studentNum){
        this.grade = new Grade(studentNum);
    }

    public void printName(){
        System.out.println("路人大学");
    }

}

class Grade{
    private C c; //C表示班级
    Grade(int studentNum){
        this.c = new C(studentNum);
    }

}

class C{
    private int studentNum;
    C(int studentNum){
        this.studentNum = 50;
    }
}

可以看到代码的整体改动还是比较大的,如果是采用IoC的思想写代码是怎样的呢?

class School{
    private Grade grade;
    School(Grade grade){
        this.grade = grade;
    }

    public void printName(){
        System.out.println("路人大学");
    }

}

class Grade{
    private C c; //C表示班级
    Grade(C c){
        this.c = c;
    }

}

class C{
    private int studentNum;
    C(){
        this.studentNum = 50;
    }
}

还是将代码运行起来,如下,和不采用IoC还是有些区别的,分别需要自己创建每个类的对象

class test{
    public static void main(String[] args) {
        C c = new C();
        Grade grade = new Grade(c);
        School school = new School(grade);
        school.printName();
    }
}

输出结果

路人大学

那这么做有什么好处呢?好像代码量还稍微变多了几行,还是上面的问题,如果要把每个班级的人数换成动态的呢?代码如下

class School{
    private Grade grade;
    School(Grade grade){
        this.grade = grade;
    }

    public void printName(){
        System.out.println("路人大学");
    }

}

class Grade{
    private C c; //C表示班级
    Grade(C c){
        this.c = c;
    }

}

class C{
    private int studentNum;
    C(int studentNum){
        this.studentNum = studentNum;
    }
}

写main方法,将代码运行起来

class test{
    public static void main(String[] args) {
        C c = new C(66);
        Grade grade = new Grade(c);
        School school = new School(grade);
        school.printName();
    }
}

可以看到只需要改C这个类就好了,而不是向之前每个类都要改,其实看到这里也很好理解,这是因为这种写法使得类与类之间的耦合性降低了。

在回想之前说的IoC的定义,控制反转,将创建对象的权力从程序中交给Spring,这里是将创建对象的权力从类中交到了main方法中,因为上面说的还没有Spring什么事,只是介绍IoC的思想。

在Spring框架中,mian方法中创建对象的活是由Spring做的,程序员只需要通过Xml或者注解的形式进行配置就行了。

IoC还有一种说法就是DI(Dependency Injection),依赖注入。至于IoC和DI的关系有一种说法是,IoC是一种思想,而DI是IoC的一种实现方式。那什么是依赖注入呢?其实很好理解,就是将你现在需要的对象给你注入进来,比如上述代码中,Grade类中需要C对象,就可以搞个注解或者Xml配置文件,告诉Spring需要C对象,让他创建好了注入进来。

下面就用Spring的Xml方式来配置下上面的代码,这里还需要搞清楚一个bean的概念,什么是bean呢,其实就是Java对象,上面main方法代码中的c、grade、school都可以看成是一个bean,而Spring就负责管理这些bean及它们之间的依赖关系,如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
    <bean id="c" class="dao.C">
        <constructor-arg value="66"/>
    </bean>

    <bean id="grade" class="dao.Grade">
        <constructor-arg ref="c"/>
    </bean>

    <bean id="school" class="dao.School">
        <constructor-arg ref="grade"/>
    </bean>

可以看到每个类都在这里对应着一个bean,并且对每个类的有参构造方法传了相应的参数,这个bean的配置参数也决定了所创建对象的属性。其中bean的可以理解为对象名,class可以理解为该对象是实现的哪个类,constructor-arg可以理解为创建对象时构造函数传的值,可以看到c对象的人数是66,grade对象依赖c对象,school对象依赖grade对象。

其实它们就是对应着如下代码

 C c = new C(66);
 Grade grade = new Grade(c);
 School school = new School(grade);

然后在把main方法改下就可以运行了,主要时加载下上面的这个xml文件,然后直接从spring拿到school对象就可以了,因为都在xml文件中配置好了

class test{
    public static void main(String[] args) {
        //初始化Spring容器ApplicationContext,加载配置文件
        ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
        School school = application.getBean(dao.School.class);
        school.printName();
    }
}

输出

路人大学

上述介绍的依赖注入方法是构造方法注入,还有两种其他的依赖注入方法,后面还会介绍。

其实写到这里,我相信很多小伙伴可能是理解IoC的好处,但spring的好处却没有get到,因为好像配置xml文件也没比自己一个一个new对象省下多少事,其实还会有基于注解的配置方式,使用熟练会使得开发简化挺多,及springboot会更大程度上简化配置。


点击面试手册,获取本站面试手册PDF完整版