1、问题引入
在说Spring之前,首先来回顾一下我们之前学习的javaSE部分的知识,假如我们现在有一个需求:现在有两个类(A类和B类),我们需要在第二个类(B)中调用第一个类(A)的某些方法,用我们以前学过的知识,肯定是在第二个类中实例化一个A类的对象,然后用对象调用对应的方法,具体操作如下:
但是这么做并不是一个好的方法,因为这样做我们可以理解为是A类 ‘污染’ 了B类,或者说是A类 ‘ 侵入 ’ 了B类。我们可以这么理解,假如现在一个项目中有很多个类都用这种方式调用了A类的方法,但是随着技术的更新有一天我们发现A类现在已经毫无存在的价值了,我们有了更好的方式去代替它,但是如果这个时候要删掉A类,那么每一个调用了A类这个方法的类都需要手动去修改源代码。这样就会给我们带来很大的麻烦。因此,我们应该用另一种更好的方法区代替上面的操作。
这个时候我们可能会想到使用接口,让A、B两个类实现同样的一个接口,然后覆写接口里面的方法,这样看起来似乎也没有多大的问题:
这样看起来是和A类没有多大的关系,但是使用这种还是摆脱不了new一个对象的命运。和这个比起来,我们更加希望:假如现在有这样一个 ‘接口’ ,当我们需要哪一个类的对象的时候,由这个特殊的接口直接给我们返回这个对象的实例,而不用让我们自己手动去new一个对象,增加代码的耦合度。比如:
其实上面这种思想就是Spring中的IOC/DI思想。
2、IOC 和 DI
2.1 什么是IOC和DI
IOC的全称为 Inversion of Control ,顾名思义就是控制反转。
在传统方法中,当一个类中需要另一个类的实例化对象,通常都是自己手动去new一个,而有了IOC之后,IOC中有一个容器,这个容器就相当于上面提到的“特殊的接口”(注意:容器不等于接口,我这里说它是一个特殊的接口,只是举个例子方便描述它的功能而已)。这个容器负责创建实例化对象,然后再在适当的时候主动将对象给需要依赖这个对象的类。
所谓的控制反转:
- 控制的就是上面说的创建对象的过程(也就是new的过程);谁控制谁 —— IOC容器控制对象的创建
- 反转:有反转就有正转,像我们的传统方法,自己需要什么就自己动手去创建什么,这个就是正转;而通过容器控制对象的创建以及依赖对象的注入,这个过程就是反转。因为是容器帮我们查找并注入依赖对象,所以对象只是被动的接收依赖对象,所以叫做反转。反转的就是依赖对象获取的过程。
DI的全称为Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
依赖注入和控制反转之间的关系:
依赖注入和控制反转其实描述的是同一个东西,只是从不同的角度来描述而已。控制反转就是为一个对象动态的提供它所需要的另一个对象,而这个过程是通过依赖注入来实现的。比如:B类需要A类的一个对象,然后容器new一个A的对象,在适当的时候,就像打针一样,把A的对象注射到B里面,这样就实现了依赖注入。
IOC不是什么新技术,而是一种思想。下面在通过一个简单的例子去理解IOC和传统方法的区别;
就拿找女朋友来说,刚开始,我们是在茫茫人海中自己去寻找,我们自己知道我们需要的是怎么样一个人,当我们发现一个目标之后,开始自己私下去打听她的qq,微型,手机号......制造巧合去认识她,然后投其所好......这一系列的过程都需要我们自己去操作,然而,现在有了IOC,就相当于是一个婚介所,他们那里存了整个村子的男男女女的资料,这个时候我们只需要将我们的要求告诉他们,然后让他们找到合适的对象之后再主动给我,如果她给我的这个对象合适了,我们就谈谈恋爱、然后结婚......如果不合适,就抛出异常。这整个过程不用我们自己去操作,而是交给了婚介所这样的一个容器去控制整个过程。Spring也就是这个思想:所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
3、在代码中简单体验IOC和DI
继续使用上面的A,B两个类:
A类:
在配置文件中配置A类
在B类中获取A类的实例对象
运行结果:
如果是新手,看来这个可能还是一脸茫然,不知道是怎么回事,下面会有完整的Spring项目创建以及IOC和DI的实例;
4、Spring属性实例化案例
使用工具:IDEA+maven
首先,创建一个maven项目(前提是你电脑中已经安装好了maven)
早maven项目的pom文件中导入依赖的jar包:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
在resource文件下创建一个配置文件(.xml) (如 applicationContext.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"
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.xsd">
<!--在这里配置类、属性信息-->
</beans>
这是下面例子的目录结构:
几个类结构:
import java.util.*;
/**
* 测试各种数据类型的属性注入
*/
public class MyTest {
private Integer intVal ; // 整形数据
private String strVal ; // String 数据
private String[] strArr ; // 数组类型
private List list ; // List集合
private Set set ; // Set集合
private Map map; // Map集合
private User user; //自定义类型
private Date date; // 日期类型
// 为每一个属性添加一个get和set方法 通过set方法实现属性注入,所以必须要有set方法
public void setIntVal(Integer intVal) {
this.intVal = intVal;
}
public void setStrVal(String strVal) {
this.strVal = strVal;
}
public void setStrArr(String[] strArr) {
this.strArr = strArr;
}
public void setList(List list) {
this.list = list;
}
public void setSet(Set set) {
this.set = set;
}
public void setMap(Map map) {
this.map = map;
}
public void setUser(User user) {
this.user = user;
}
public void setDate(Date date) {
this.date = date;
}
// 覆写toString方法用于打印属性信息
@Override
public String toString() {
return " intVal = " + intVal +
"\r\n strVal = '" + strVal + '\'' +
"\r\n strArr = " + Arrays.toString(strArr) +
"\r\n list = " + list +
"\r\n set = " + set +
"\r\n map = " + map +
"\r\n date = " + date +
"\r\n user = " + user ;
}
}
/**
* 用于测试自定义属性注入的User类
*/
public class User {
private Integer age ;
private String name ;
public void setAge(Integer age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
// 覆写toString方法用于打印信息
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
4.1 通过set方法实例化:
<?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.xsd">
<!--在这里配置类、属性信息-->
<bean id="test" class="per.fei._3SpringProjectTest.MyTest">
<!--整形的注入-->
<property name="intVal">
<value>18</value>
</property>
<!--整形也可以这么弄-->
<!--这里写的是String ("18"),而类中的类型是Integer ( = 18),spring内部实现了从String到Integer的转换-->
<!--<property name="intVal" value="18"/>-->
<!--String类型-->
<property name="strVal" value="hahaha"/>
<!--数组类型-->
<property name="strArr">
<list>
<value>strArr_1</value>
<value>strArr_2</value>
<value>strArr_3</value>
</list>
</property>
<!--list就是一个动态数组,所以和数组基本一样-->
<property name="list">
<list>
<value>list_1</value>
<value>list_2</value>
<value>list_3</value>
</list>
</property>
<!--set类型的数据-->
<property name="set">
<set>
<value>set_01</value>
<value>set_02</value>
<value>set_03</value>
</set>
</property>
<!--map类型-->
<property name="map" >
<map>
<entry key="1" value="no_1"/>
<entry key="2" value="no_2"/>
<entry key="3" value="no_3"/>
</map>
</property>
<!--Date类型不能直接像下面这么弄,要先写一个类型转换器(MyConvert.java),
因为Spring虽然在上面说实现了从String到Integer的类型转换,-->
<!--但是没有实现从String到Date的类型转换,需要我们自己实现一个转换器-->
<property name="date" value="2019-5-21"/>
<!--自定义类型-可以先配置对应的自定义类型的bean,然后通过ref(反射)直接获取对应的自定义类型-->
<property name="user" ref="user"/>
</bean>
<!--配置User类-->
<bean id="user" class="per.fei._3SpringProjectTest.User">
<property name="name" value="张三"/>
<property name="age" value="22"/>
</bean>
<!--自定义属性编辑器到Spring中-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map >
<entry key="java.util.Date" value="per.fei._3SpringProjectTest.MyConvert"/>
</map>
</property>
</bean>
</beans>
测试结果:
4.2 通过构造方法实例化:
(这里需要把上面的MyTest类中的set方法换成构造方法,其余类结构的不变)
MyTest.java
import java.util.*;
/**
* 测试各种数据类型的属性注入
*/
public class MyTest {
private Integer intVal ; // 整形数据
private String strVal ; // String 数据
private String[] strArr ; // 数组类型
private List list ; // List集合
private Set set ; // Set集合
private Map map; // Map集合
private User user; //自定义类型
private Date date; // 日期类型
// 添加构造方法
public MyTest(Integer intVal, String strVal, String[] strArr, List list, Set set, Map map, User user, Date date) {
this.intVal = intVal;
this.strVal = strVal;
this.strArr = strArr;
this.list = list;
this.set = set;
this.map = map;
this.user = user;
this.date = date;
}
// 覆写toString方法用于打印属性信息
@Override
public String toString() {
return " intVal = " + intVal +
"\r\n strVal = '" + strVal + '\'' +
"\r\n strArr = " + Arrays.toString(strArr) +
"\r\n list = " + list +
"\r\n set = " + set +
"\r\n map = " + map +
"\r\n date = " + date +
"\r\n user = " + user ;
}
}
配置文件:
<?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.xsd">
<!--在这里配置类、属性信息-->
<bean id="test" class="per.fei._3SpringProjectTest.MyTest">
<!--int型-->
<constructor-arg name="intVal" value="18"/>
<!--String类型-->
<constructor-arg name="strVal" value="hahaha"/>
<!--数组类型-->
<constructor-arg name="strArr">
<list>
<value>strArr_01</value>
<value>strArr_02</value>
<value>strArr_03</value>
</list>
</constructor-arg>
<!--list类-->
<constructor-arg name="list">
<list>
<value>list_01</value>
<value>list_02</value>
<value>list_03</value>
</list>
</constructor-arg>
<!--set类型-->
<constructor-arg name="set">
<set>
<value>set_01</value>
<value>set_02</value>
<value>set_03</value>
</set>
</constructor-arg>
<!--map类型-->
<constructor-arg name="map">
<map>
<entry key="mapKey_01" value="mapVal_01"/>
<entry key="mapKey_02" value="mapVal_02"/>
<entry key="mapKey_03" value="mapVal_03"/>
</map>
</constructor-arg>
<!--自定义类User-->
<constructor-arg name="user" ref="user"/>
<!--Date类-->
<constructor-arg name="date" value="2019-5-21"/>
</bean>
<!--配置User类-->
<bean id="user" class="per.fei._3SpringProjectTest.User">
<property name="name" value="张三"/>
<property name="age" value="22"/>
</bean>
<!--自定义属性编辑器到Spring中-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map >
<entry key="java.util.Date" value="per.fei._3SpringProjectTest.MyConvert"/>
</map>
</property>
</bean>
</beans>
Test类运行结果: