轻松理解Java Spring 核心功能之一: 控制反转容器(IoC Container)

控制反转容器(IoC Container)是Spring的一个重要基础功能,不喜欢新名词的同学,姑且理解为组件装配容器.

为什么需要Spring容器 ?(Why Spring Container)

要理解Spring的设计精神,先来看看Spring的设计初衷.

先看看没有Spring之前,我们怎么做.
以一个现实的情况说明.大家应该都有过购买手机的经历.
收到手机后,打开包装, 给手机装上SIM卡,装上电池,开机 ...

我们用java面向对象的思想做设计:
手机,SIM卡,电池, 这是三个对象,也可称为实体,物体,组件,零件
名称不重要,反正这是三个东西,自然对应三个java 对象.
1, Battery.java

package demo;

public class Battery{
    private String power;
    public Battery(String power){
        this.power = power;
    }
    public String getPower(){
        return this.power;
    }
}

2, Simcard.java

package demo;

public class Simcard{
    private String number;
    public Simcard(String  number){
        this.number = number;
    }
    public String getNumber(){
        return this.number;
    }
}

3, Mobile.java

package demo;
public class Mobile{
    private String brand;
    private Battery battery;
    private Simcard simcard;

    public Mobile(String brand){
        this.brand = brand;
    }
    public void setBattery(Battery battery){
        this.battery =  battery;
    }
    public void setSimcard(Simcard simcard){
        this.simcard = simcard;
    }
    public void poweron(){
        System.out.println("Mobile: " +  this.brand + " powered on");
        System.out.println("Battery: " + this.battery.getPower() );
        System.out.println("Simcard: " + this.simcard.getNumber() );

    }
}

不使用Spring 容器

4, 运行程序 Main.java

package demo;

public class Main{
     public static void main(String[] args) {
        //创建零件
        Mobile mobile = new Mobile("XiaoMI1");
        Battery battery =  new Battery("2000mA");
        Simcard simcard = new Simcard("13612345678");
        //组装零件
        mobile.setBattery(battery);
        mobile.setSimcard(simcard);
        //使用
        mobile.poweron();
     }
}

以上程序不使用Spring,运行良好.
因为这里只有三个零件,创建和组装都没有问题.
如有一个设备由1000个零件,创建和组装过程就很繁琐.
试想,我们网购了一辆山地自行车,要把它装配起来,我们首先会看货品清单和装配说明书.

Spring 容器就是为创建和组装零件而设计的.

模拟简单容器

在介绍Spring 容器之前, 先介绍一下容器的概念,初学者会对容器概念感到陌生.
现实生活中,装有糖果的盒子就是容器,Java里的容器和现实生活类似,只不过装的是对象实例.
这里对 Main.java 做些修改以解释容器概念
4, 运行程序 Main.java

package demo;

public class Main{
     public static void main(String[] args) {
          //创建容器
          Object[] container = new Object[3];
          //创建和组装实例
          Mobile mob = new Mobile("XiaoMI1");
          Battery bat = new Battery("2000mA");
          Simcard sim = new Simcard("13612345678");
          mob.setBattery(bat);
          mob.setSimcard(sim);
          //放入容器
          container[0] = mob;
          container[1] = bat;
          container[2] = sim;
          //从容器中获取实例
          Mobile mobile = (Mobile) container[0];
          mobile.poweron();
     }
}

以上程序创建数组作为容器, 创建和组装实例后放入容器, 使用时,从容器中获取实例.
这个程序看起来有些多此一举,确体现了容器的主要特性.
这里,我们手动完成了 创建容器->创建和组装实例->放入容器的工作.
使用Spring后,这些工作就可以由Spring来完成.
这里,我们使用对象数组作为容器, 通过数字下标获取对象, Spring 使用ConcurrentHashMap 作为容器, 通过字符串ID获取对象. (事实上ConcurrentHashMap 内部也是把对象包装一下放到数组里)

这里,零件之间的装配关系通过人工完成,而Spring通过配置文件来描述零件之间的装配关系.
Spring的配置文件使用XML格式描述组件清单和装配关系,这个配置文件相当于现实生活中的货品清单和装配说明书.

使用Spring容器

使用Spring容器时,需要一个配置文件(货品清单) manifest.xml
5, manifest.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-4.0.xsd">

    <bean id="battery1" class="demo.Battery">
            <constructor-arg name="power" value="3000mA" />
    </bean>
    <bean id="simcard1" class="demo.Simcard">
            <constructor-arg name="number" value="13512345678" />
    </bean>
    <bean id="mobile1" class="demo.Mobile">
            <constructor-arg name="brand" value="Xiaomi" />
             <property name="Simcard" ref="simcard1"/>
            <property name="Battery" ref="battery1"/>
    </bean>

</beans>

这里的bean 就是对象实例的意思,java里所有对象实例都可称为bean
这个配置文件,简单的说就是一个bean的列表. 每个bean通过XML配置其创建初始值或是装配对象. id属性,是为了以后便于从容器中获取.
这个配置文件里battery1,simcard1 我们设定的构造参数,而mobile1, 我们设定了构造参数的同时还装配了具体的Battery和Simcard.

4, 运行程序 Main.java( 使用Spring)

package demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main{
     public static void main(String[] args) {
          ApplicationContext context= new ClassPathXmlApplicationContext("manifest.xml");
          Mobile mobile = (Mobile) context.getBean("mobile1");
          System.out.println("************************");
          mobile.poweron();
    }
}

这里的context, 简单的理解就是对象容器, Spring 通过分析配置文件完成对象创建,装配的工作.
使用时,通过ID获取容器对象.

Spring容器通过配置文件来描述零件清单和装配关系,装配关系变动时,修改配置即可,不用重新编译代码.
对于简单的例子,看不出Spring容器的优势.
当对于零件很多,装配关系复杂多变的系统,就能体现Spring的优势.

Spring 容器除了使用xml来配置,还可以使用注解(annotation)来配置. 注解配置,这里不在展开细说,总之,两者的功能是一样的,看个人喜好使用.

运行源代码

git clone https://github.com/huanghq/SpringContainerExample.git
cd SpringContainerExample/
./gradlew run
(windows 用户:gradlew.bat run)

************************
Mobile: Xiaomi powered on
Battery: 3000mA
Simcard: 13512345678

运行源代码要求Java1.7 环境,支持跨平台.
使用gradle 下载依赖包,编译运行.不熟悉gradle的同学没关系.
只需专注上述java文件和配置文件,
第一次运行需要下载gradle和java依赖包,有些慢,耐心等待.

对控制反转(IOC)概念感兴趣的同学,参看下文:
Java开发中的依赖注入(DI)和控制反转(IOC)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容