Spring容器是Spring框架的核心,通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。容器将创建对象,并且管理对象的整个生命周期。IOC容器更是具有依赖注入的功能,可以实例化、配置应用程序中的对象及建立这些对象建的依赖。那么问题来了,什么是IOC和依赖注入?
一、IOC:Inversion Of Control,控制反转(或DI:Dependency Inject 依赖注入)。
第一次接触这个名词感觉怪怪的,经过一段时间的学习才发现这个名词是有一定意义的。扯的有些远了,下面进入正题。
控制反转(IOC):
控制的是什么? 控制的是类的对象。
何为反转? 原来由人主动通过new实例化对象的事情,转交给Spring负责。
这样做有什么好处? 人不需要管理这些对象,解除了对象管理与人之间的耦合。
依赖注入(DI):
控制反转,又叫依赖注入(DI)。 DI是IOC的具体实现,Spring容器通过依赖注入的方式来管理Bean之间的依赖关系。
每个基于应用程序的java都有几个类对象,依赖注入有助于把这些类粘合在一起,同时保持他们独立。
下面我们来看看具体的例子,所用到的lib包:commons-logging-1.1.3.jar(日志包),junit-3.8.1.jar(测试包),spring-beans-4.1.6.RELEASE.jar、spring-context-4.1.6.RELEASE.jar、spring-core-4.1.6.RELEASE.jar、spring-expression-4.1.6.RELEASE.jar(启动Spring的四个基本包)。
1.1 建立实体类com.learn.pojo.Fruit。
package com.learn.pojo;
public class Fruit {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Fruit [name=" + name + "]";
}
}
1.2 在src下创建Spring配置文件,配置文件的名称自定义,这里我使用的配置文件名称:spring.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="fruit" class="com.learn.pojo.Fruit"></bean>
</beans>
1.3 编写测试类进行测试。
package com.learn.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.learn.pojo.Fruit;
import junit.framework.TestCase;
public class SpringTest extends TestCase {
public void testSpringIoc() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Fruit fruit = context.getBean("fruit", Fruit.class);
System.out.println("创建类的信息:" + fruit);
//context.getBeanDefinitionNames(); 查询Spring的IOC容器创建的所有的类名称
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println("类名称:" + name);
}
}
}
1.4 结果如下:
小结:从该例可以清楚的看到,通过读取spring.xml配置文件里的信息,Spring的容器可以创建并管理对象。没有通过new实例化对象。这就是Spring的IOC所完成的事情,即原先由程序员主动通过new实例化对象的事情,现在转交给Spring。
二、Spring实例化对象的两种方式
2.1 通过构造器实例化对象。
默认情况下使用无参构造器创建对象,如上面的例子所示。若通过有参构造器创建对象,需要在配置文件中添加配置事例如下:
创建一Apple类。
package com.learn.pojo;
public class Apple {
private String color;
public Apple(String color) {
super();
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Apple [color=" + color + "]";
}
}
将Apple类作为Fruit类的属性。
package com.learn.pojo;
public class Fruit {
private String name;
private Apple apple;
public Fruit(String name, Apple apple) {
super();
this.name = name;
this.apple = apple;
}
public Apple getApple() {
return apple;
}
public void setApple(Apple apple) {
this.apple = apple;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Fruit [name=" + name + ", apple=" + apple + "]";
}
}
此时若还用1.2的配置文件会报java.lang.NoSuchMethodException的错误,原因是此时只有存在有参的构造方法,而在实体类中不存在无参构造器。配置文件如下:
<?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">
<!-- 配置Apple并实例化信息
index 指定构造器参数的位置
name 指定构造器参数的名称
value 指定构造器参数的值
-->
<bean id="apple" class="com.learn.pojo.Apple">
<constructor-arg index="0" name="color" value="红色"></constructor-arg>
</bean>
<!-- 配置Fruit并实例化信息 -->
<bean id="fruit" class="com.learn.pojo.Fruit">
<constructor-arg index="0" name="name" value="水果" type="java.lang.String"></constructor-arg>
<constructor-arg index="1" ref="apple"></constructor-arg>
</bean>
</beans>
测试代码与1.3的内容一致,得到结果如下:
小结:如上面的例子,通过构造器实例化对象,同时我们还可以这么理解依赖注入:
当一个类(Fruit)需要依赖另一个类(Apple)对象,把类Apple赋值给Fruit的过程就叫依赖注入。
2.2 通过Setter设值实例化对象。
创建实体Fruit 类:
package com.learn.pojo;
public class Fruit {
private String name;
private Apple apple;
public Fruit() {
super();
}
public Fruit(String name, Apple apple) {
super();
this.name = name;
this.apple = apple;
}
public Apple getApple() {
return apple;
}
public void setApple(Apple apple) {
this.apple = apple;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Fruit [name=" + name + ", apple=" + apple + "]";
}
}
创建实体Apple 类:
package com.learn.pojo;
public class Apple {
private String color;
public Apple() {
super();
}
public Apple(String color) {
super();
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Apple [color=" + color + "]";
}
}
配置文件内容如下:
<?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">
<!-- 配置Apple并实例化信息 -->
<bean id="apple" class="com.learn.pojo.Apple">
<property name="color" value="青色"></property>
</bean>
<!-- 配置Fruit并实例化信息 -->
<bean id="fruit" class="com.learn.pojo.Fruit">
<property name="name" value="水果"></property>
<property name="apple" ref="apple"></property>
</bean>
</beans>
运行结果:
注意:使用setter设值注入时,需要用到无参的构造器(需要先创建实例,再设值注入)。基于构造函数和基于 setter 方法的 DI,然而使用有强制性依存关系的构造函数和有可选依赖关系的 setter是一个比较好的做法。
小结:基于构造函数注入和基于设值函数注入中的 Spring.xml 文件的区别。唯一的区别就是在基于构造函数注入中,我 们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素。
第二个你需要注意的点是,如果你要把一个引用传递给一个对象,那么你需要使用 标签的 ref 属性,而如果你要直接传递一个值,那么你应该使用 value 属性。
三、Spring对Bean的常用属性赋值
3.1 创建实体类
package com.xzp.spring.pojo;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Person {
private int id;
private String name;
private Set<Integer> sets;
private Map<String, String> map;
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Set<Integer> getSets() {
return sets;
}
public void setSets(Set<Integer> sets) {
this.sets = sets;
}
public List<Integer> getLists() {
return lists;
}
public void setLists(List<Integer> lists) {
this.lists = lists;
}
private List<Integer> lists;
public Person(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", sets=" + sets + ", map=" + map + ", lists=" + lists + "]";
}
}
3.2 配置文件内容如下
<?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的各属性赋值 -->
<bean id="per" class="com.xzp.spring.pojo.Person">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="张三"/>
<property name="sets">
<set value-type="java.lang.Integer">
<value>1</value>
<value>2</value>
<value>3</value>
</set>
</property>
<property name="lists">
<list value-type="java.lang.Integer">
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
<property name="map">
<map>
<entry key="zs" value="张三"></entry>
<entry key="ls" value="李四"></entry>
</map>
</property>
</bean>
</beans>
3.2 测试内容如下:
package com.learn.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.xzp.spring.pojo.Person;
import junit.framework.TestCase;
public class SpringTest extends TestCase {
public void testSpring() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = ac.getBean("per", Person.class);
System.out.println(person);
}
}
3.3 输出结果