Spring_IoC
一、IoC相关概念简介
1、理解【控制】和【依赖】的概念
IoC即控制反转、依赖注入 。—— OOAD依赖倒置
【控制】和【依赖】都代表对象之间的关联关系,而且他们在Spring中是一对近义词,控制和依赖几乎是并存的。
例如,现有如下需求:汽车的功能是行驶,司机的功能是驾驶汽车,令汽车行驶。
我们可以通过代码描述上述关系:
public class Car {
// 汽车类
private String name; // 品牌
private Double price; // 价格
public void run(){
// 汽车行驶方法
System.out.println("Car : I'm running!");
}
}
public class Driver {
// 司机类
public Car car;
public void drive() {
// 司机驾驶方法
car.run();
}
}
上述案例中,司机可以调用驾驶方法,令其包含的汽车对象执行行驶方法。需要关注的问题是,car对象是如何被实例化的。
在传统的Java编程中,我们习惯于将这个car对象通过new的方式创建出来。那么什么时候去new它呢?答案是不唯一的。例如:
1)声明的同时实例化
public class Driver {
Car car = new Car();
public void drive(){
car.run();
}
}
2)在调用时进行实例化
public class Driver {
Car car;
public void drive(){
car = new Car();
car.run();
}
}
上述两种方式,Car对象被创建的时机不同。但是相同的是,new Car()的过程都发生在Driver类中。这样的形式,我们称之为Driver在自己的【代码内部】,控制了一个Car。
注意,这里的【控制】强调的是控制Car对象的创建,而并非Car对象方法的调用。
而【依赖】是指,此时Driver的正常工作需要依赖于Car。换言之,Driver和Car产生了耦合。
耦合是指对象之间相互依赖的程度。在软件设计和开发中,我们要尽可能地降低类之间的耦合度。一个类如果出现了异常,或发生了改变,那么依赖于它的其他所有类可能都不能正常运行,导致程序大面积的瘫痪,也称为雪崩效应。
2、IoC 控制反转
IoC即Inversion of Control,控制反转。
可以理解为“反转”对象的控制关系,或“改变”对象的控制关系。
传统的Java SE编程中,对象之间的关系是在【对象内部】直接控制,而IoC正是改变了这样的控制关系,改为由【外部容器】来控制。
大致的过程是,我们不再像原来那样将new操作写死在类中,而是将对象之间的关联关系定义在一个xml配置文件中。在程序运行期间,再由容器根据配置文件中定义的规则管理对象的生命周期,并建立对象之间的关联关系。
也可以这样理解,传统的写法是在编译期间就将对象关系规定死,程序运行时一定会按照这样的规则去执行。而Spring IoC的做法是在编译期间将对象全部分离,当进入到运行阶段再将他们动态结合到一起。
IoC体现了好莱坞原则,即“不要打电话过来,我们会打给你”。
3、依赖注入和依赖查找
public class Student{
private int id;
private String name;
private Teacher teacher;
}
Student stu = new Student();
Teacher teacher = new Teacher();
stu.setTeacher(teacher);
理解两个名词:
1)DI(Dependency Injection) 依赖注入 (要求掌握,核心内容)
依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。
2)DL(Dependency Loopup) 依赖查找 (了解即可)
容器创建对象并提供回调接口和上下文环境给组件,需要时通过接口从容器中查找对象(理解即可)
DI和DL的关系:
假设现在需要使用A对象,A对象中需要配置一个B对象。那么我们在容器中配置一个A对象,配置一个B对象,并声明B对象是A对象的一个属性。这个过程叫做依赖注入。
如果你想从容器中取出某个对象对其进行操作,可以调用context组件的相关方法,根据特定的方式获取容器中的Bean。这个过程叫做依赖查找。
DI、DL和IoC的关系:
IoC是一种思想,而DI和DL是实现这种思想具体的方式。
从实际角度出发分析,IoC解决了对象由谁来创建、对象之间存在什么何种关联关系的问题
而DI解决了对象之间的关联关系如何建立的问题
IoC和DI总结:
简而言之,原本我们在代码中会频繁地new对象,现在使用IoC(DI),我们会观察到类似这样的效果:只需要在类中定义需要什么对象作属性,你也可以在方法中让这个对象去调用方法。而这样做并不会抛出空指针异常,因为IoC容器发现你需要用到这个对象时会先帮你进行注入。
二、Core、Beans、Context组件概念
1、组件及原理简介
Spring中包含诸多组件,其中Core、Beans、Context是最核心的三个组件。
它们组合起来,就实现了Spring中最核心的功能——IoC。
IoC是Spring功能的根基。如果没有IoC的存在,就不可能实现AOP、Web等其他上层功能。
通俗地来讲,三个组件的功能及角色如下:
-
Beans:组件(Bean的复数形式)
-
Context:容器(用来包含组件)
-
Core:工具
首先,Bean是Spring中最为核心的概念。Spring框架可以说就是在面向Bean编程。Bean之于Spring相当于Object之于OOP。在Spring中,所有的Java对象都是Bean。
IoC的核心概念是将Bean配置到容器中,而这个容器就是由Context来充当。在编译阶段,我们以xml文件的方式定义容器配置,声明Bean相关内容。运行期间,Spring会解析xml容器配置文件,将其中的内容封装成一个Context对象。我们可以通过这个Context对象获取到容器中的Bean。
Core是处理容器和Bean的一些工具和方法,如果把Core改称为Util就很好理解了。
如果说Context是一个舞台,那么Bean就是这个舞台上的演员。而Core就是演员演出所用的道具。
2.容器xml配置文件
<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-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<bean></bean>
<bean></bean>
</beans>
三、IoC常用jar包
如果需要在项目中使用基本的IoC功能,通常需要引入如下jar文件(版本信息省略):
spring-beans.jar
spring-context.jar
spring-core.jar
spring-expression.jar
junit.jar
commons-logging.jar
四、Bean对象的定义和获取
1、在容器中声明对象
【类声明】Car.java:
public class Car {
private String name; // 车名
private double price; // 价格
getters/setters...
}
【配置文件】bean.xml:
<bean id="mycar" class="com.briup.day01.Car"></bean>
2、在代码中获取容器中的对象
创建测试类BeanTest.java:
public class SetterTest {
@Test
public void bean_test() {
// 创建容器对象
ApplicationContext context = new ClassPathXmlApplicationContext(
"com/briup/day01/bean.xml");
// 获取Bean
Car car = (Car) context.getBean("mycar");
// 打印输出
System.out.println(car);
}
五、IoC注入方式
什么是注入?
【注入】也称装配,即为对象的属性赋值。
通过注入,可以为容器中的基本属性赋值,例如学生学号、姓名、年龄等,也可以通过注入来创建对象之间的关联关系。例如,在Driver对象中注入一个Car对象,此时由Car对象充当Driver对象的属性。
IoC实现注入有三种常用方式:
-
set注入
-
构造器注入
-
接口注入
比较常用的是set和构造器注入,接口注入用于引入外界系统资源,例如JNDI。
1.set注入
要求:要注入的属性必须包含对应的set方法。
可以注入的类型:①基本数据类型和String类型 ②对象类型 ③集合类型
1)基本数据类型
【配置文件】bean.xml:
<bean id="mycar" class="com.briup.day01.Car">
<property name="name" value="兰博基尼">
<property name="price">
<value>10.5</value>
</property>
</bean>
bean中的属性,id为该对象在容器中起一个名字,class指定对象类型。
除了id之外还可以使用name属性为Bean起名字。
id和name区别在于,id是唯一标识,并且要求比较严格。
容器会检查id属性值是否符合规(名字是否重复、是否以数字开头、是否包含空格等等),但是name不会检查这些内容。
<property>
元素用来为属性赋值,每个<property>
元素为对象的一条属性赋值。
name
属性用来指定属性名,例如name="price"
代表当前<property>
是为了给Car对象的price属性赋值。
value
属性是具体的值,也可以定义为<property>
中的子元素。
2)对象类型
【配置文件】bean.xml:
<bean id="mycar" class="com.briup.day01.Car">
<property name="name" value="兰博基尼">
<property name="price">
<value>10.5</value>
</pro