控制反转容器(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)