Spring IoC入门
本小记中学习的目标
-
Spring IoC的基本概念的理解
-
Spring IoC容器的理解
-
依赖注入的类型(构造方法、setter方法)的实现方法
一、Spring IoC的基本概念
IoC:Inversion of Control,控制反转,它是Spring框架的核心,目的是用来减少程序模块间的耦合问题。
DI:Dependency Injection,依赖注入,它是IoC的另一种说法。
在Java编程中,如果某个对象,需要调用另一个对象(这个对象被称作为被依赖对象)时,首先我们想到的最为直观简单的编程方式就是使用【new 被调用者】的方式来创建对象。这种方式可以满足当前的即时需求,但是在未来一但被调用者升级修改了有可能影响到new的方式,这样就增加了调用者与被调用者之间的耦合性。
说完上面的例子看到了它不友好的一面,那Spring框架出来后在这个方面为程序员提供了更好的解决方案,这个时候被调用者的对象实例并不是由调用者自行来new出来,而是由Spring容器来创建。Spring容器会控制调用者与被调用者的关系,把这种关系的控制由原来的调用者使用代码控制变为交由Spring容器来控制,这种控制权发生了相应的转换,这也就是Spring的控制反转。
说到这里,Spring容器的任务就是把被依赖的对象赋值给调用者的成员变量,相当于为调用者注入它依赖的对象,这也就是Spring的依赖注入。
Spring IoC
理解一下Spring IoC容器设计的实现
它的实现主要依赖于如下两个接口:BeanFactory、ApplicationContext
BeanFactory接口 org.springframework.beans.factory.BeanFactory
从这个接口的命名可以看出来它是一个管理Bean的工厂,它的工作就是初始化各种Bean。
ApplicationContext接口 org.springframework.context.ApplicationContext (常用重要)
这个接口除了有BeanFactory接口的所有功能外,还额外添加了对国际化、资源访问、事件传播……的支持。
创建ApplicationContext接口实例的三种方式
方式一:使用ClassPathXmlApplicationContext创建
从这个实现的命名可以看出,这会从classpath路径(类路径,src根目录,maven工程中src/main/resources)中查找指定的xml配置文件
方式二:FileSystemXmlApplicationContext创建(一般不使用这种方式)
从这个实现的命名可以看出,它会从指定文件的绝对路径中查找xml配置文件
方式三:通过Web服务器实例化ApplicationContext容器(原理:在服务器加载时使用监听器完成)
一般是使用org.springframework.web.context.ContextLoaderListener的实现方式,这种方式需要依赖spring-web-xxx.RELEASE.jar,同时需要在web.xml中做如下配置
<!-- 配置ContextLoaderListener,用来初始化spring的配置文件 -->
<
context-param
>
<
param-name
>contextConfigLocation
</
param-name
>
<
param-value
>classpath:beans.xml
</
param-value
>
</
context-param
>
<
listener
>
<
listener-class
>org.springframework.web.context.ContextLoaderListener
</
listener-class
>
</
listener
>
一旦我们有了配置的信息并交给Spring后,Spring就知道如何把被依赖的对象注入到调用者当中,这样就不再需要通过程序new的方式来操作了
依赖注入类型
Spring IoC是通过依赖注入的方式来实现的,它的作用是在使用Spring框架创建对象时动态地把依赖的对象注入bean组件中。Spring框架的依赖注入主要有两种实现方式,一种是通过构造方法来实现;另一种是通过setter方法来注入
构造方法注入
构造方法的注入方式是Spring采用了Java反射的机制来完成的。
实例
1.新增一个Maven的jar工程
2.在pom.xml中新增如下依赖
<
dependencies
>
<!-- 添加Spring核心依赖包 Context -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-context
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- commons-logging-1.2 -->
<
dependency
>
<
groupId
>commons-logging
</
groupId
>
<
artifactId
>commons-logging
</
artifactId
>
<
version
>1.2
</
version
>
</
dependency
>
</
dependencies
>
3.新增一个Dao接口
com.xiaoxie.dao.TestDIDao
在其中新增一个接口方法
package com.xiaoxie.dao;
public
interface TestDIDao {
void sayHello();
}
4.新增TestDIDao的实现类
com.xiaoxie.dao.impl.TestDIDaoImpl
package com.xiaoxie.dao.impl;
import com.xiaoxie.dao.TestDIDao;
public
class TestDIDaoImpl
implements TestDIDao {
@Override
public
void sayHello() {
System.
out.println(
"sayHello()方法输出:DI注入类型-构造方法");
}
}
5.新增service包并在其中新增Service的接口
package com.xiaoxie.service;
public
interface TestDIService {
void sayHello();
}
6.新增service的实现包impl,并在其中新增TestDIService的实现类,并把Dao包中的实例对象做注入
package com.xiaoxie.service.impl;
import com.xiaoxie.dao.TestDIDao;
import com.xiaoxie.service.TestDIService;
public
class TestDIServiceImpl
implements TestDIService {
private TestDIDao
testDIDao;
//构造方法,用来注入TestDIDao的实例对象
public TestDIServiceImpl(TestDIDao
testDIDao) {
System.
out.println(
"调用TestDIServiceImpl构造函数!");
this.
testDIDao =
testDIDao;
System.
out.println(
"构造函数调用完成,testDIDao被赋值:" +
testDIDao);
}
@Override
public
void sayHello() {
//调用testDIDao中的sayHello方法
testDIDao.sayHello();
}
}
7.在src/main/resources下新增Spring配置文件beans.xml
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
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"
>
<!-- 把
Dao
的实现类交由Spring容器进行管理,让Spring来创建它 -->
<
bean
class=
"com.xiaoxie.dao.impl.TestDIDaoImpl"
id=
"testDIDao"
/>
<!-- 在Service中使用构造方法进行注入依赖有对象 -->
<
bean
class=
"com.xiaoxie.service.impl.TestDIServiceImpl"
id=
"testDIService"
>
<
constructor-arg
name=
"testDIDao"
ref=
"testDIDao"
/>
</
bean
>
</
beans
>
在上面的配置中我们可以看到在将TestDIServiceImpl交由Spring管理时,同时使用了<constructor-arg>标签来对其构造函数中属性进行实参传递以达到初始化它依赖对象的目的
8.创建测试包及测试类
在src/test/java中新增测试包及测试类如下
package com.xiaoxie.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.xiaoxie.service.TestDIService;
public
class TestDI {
public
static
void main(String[]
args) {
//加载Spring配置文件初始化Spring容器
ApplicationContext
applicationContext =
new ClassPathXmlApplicationContext(
"beans.xml");
TestDIByConstruct(
applicationContext);
}
private
static
void TestDIByConstruct(ApplicationContext
applicationContext) {
TestDIService
testDIService = (TestDIService)
applicationContext.getBean(
"testDIService");
testDIService.sayHello();
}
}
在上面的测试代码中我们可以看到在根据Spring配置文件初始化Spring容器后,获取Spring配置文件所指定的testDIService这个id指定的实例对象,同时这个时候会把其依赖的对象也初始化好。
9.运行测试类,在控制台打印的结果如下:
调用TestDIServiceImpl构造函数!
构造函数调用完成,testDIDao被赋值:com.xiaoxie.dao.impl.TestDIDaoImpl@77f80c04
sayHello()方法输出:DI注入类型-构造方法
属性setter方法注入
使用setter方法注入,Spring也是使用了Java的反射机制,它是Spring框架中常用的注入方法,利用Java Bean的setter方法来完成注入。
实例
1.在Dao的接口中新增接口方法sayHello1()
package com.xiaoxie.dao;
public
interface TestDIDao {
void sayHello();
void sayHello1();
}
2.在Dao的接口实现类中添加sayHello1()的实现方法
package com.xiaoxie.dao.impl;
import com.xiaoxie.dao.TestDIDao;
public
class TestDIDaoImpl
implements TestDIDao {
@Override
public
void sayHello() {
System.
out.println(
"sayHello()方法输出:DI注入类型-构造方法");
}
@Override
public
void sayHello1() {
System.
out.println(
"sayHello()方法输出:DI注入类型-setter方法");
}
}
3.在service接口中新增接口方法sayHello1()
package com.xiaoxie.service;
public
interface TestDIService {
void sayHello();
void sayHello1();
}
4.修改TestDIServiceImpl类的实现如下
package com.xiaoxie.service.impl;
import com.xiaoxie.dao.TestDIDao;
import com.xiaoxie.service.TestDIService;
public
class TestDIServiceImpl
implements TestDIService {
private TestDIDao
testDIDao;
//构造方法,用来注入TestDIDao的实例对象
public TestDIServiceImpl(TestDIDao
testDIDao) {
System.
out.println(
"调用TestDIServiceImpl构造函数!");
this.
testDIDao =
testDIDao;
System.
out.println(
"构造函数调用完成,testDIDao被赋值:" +
testDIDao);
}
@Override
public
void sayHello() {
//调用testDIDao中的sayHello方法
testDIDao.sayHello();
}
@Override
public
void sayHello1() {
}
}
5.在上面的工程中新增一个service实现类,在这个实现类中我们计划使用setter方法来做注入
package com.xiaoxie.service.impl;
import com.xiaoxie.dao.TestDIDao;
import com.xiaoxie.service.TestDIService;
public
class TestDIServiceImpl1
implements TestDIService {
private TestDIDao
tesDIDao1;
//setter方法
public
void setTesDIDao1(TestDIDao
tesDIDao1) {
System.
out.println(
"调用setter方法");
this.
tesDIDao1 =
tesDIDao1;
System.
out.println(
"setter方法调用完成,testDIDao1对象:" +
tesDIDao1);
}
@Override
public
void sayHello1() {
tesDIDao1.sayHello1();
}
@Override
public
void sayHello() {
}
}
6.Spring容器的配置文件中新增内容后如下
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
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"
>
<!-- 把
Dao
的实现类交由Spring容器进行管理,让Spring来创建它 -->
<
bean
class=
"com.xiaoxie.dao.impl.TestDIDaoImpl"
id=
"testDIDao"
/>
<!-- 在Service中使用构造方法进行注入依赖有对象 -->
<
bean
class=
"com.xiaoxie.service.impl.TestDIServiceImpl"
id=
"testDIService"
>
<
constructor-arg
name=
"testDIDao"
ref=
"testDIDao"
/>
</
bean
>
<!-- 在Service中使用setter方法进行注入依赖的对象 -->
<
bean
class=
"com.xiaoxie.service.impl.TestDIServiceImpl1"
id=
"testDIService1"
>
<
property
name=
"
tesDIDao1"
ref=
"testDIDao"
></
property
>
</
bean
>
</
beans
>
7.在测试类中测试方法中新增测试方法及在main方法中对方法进行调用
package com.xiaoxie.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.xiaoxie.service.TestDIService;
public
class TestDI {
public
static
void main(String[]
args) {
//加载Spring配置文件初始化Spring容器
ApplicationContext
applicationContext =
new ClassPathXmlApplicationContext(
"beans.xml");
TestDIByConstruct(
applicationContext);
System.
out.println(
"--------setter方法进行注入-------");
TestDIBySetter(
applicationContext);
}
private
static
void TestDIByConstruct(ApplicationContext
applicationContext) {
TestDIService
testDIService = (TestDIService)
applicationContext.getBean(
"testDIService");
testDIService.sayHello();
}
public
static
void TestDIBySetter(ApplicationContext
applicationContext) {
TestDIService
testDIService = (TestDIService)
applicationContext.getBean(
"testDIService1");
testDIService.sayHello1();
}
}
8.运行测试类,在控制台打印结果如下
调用TestDIServiceImpl构造函数!
构造函数调用完成,testDIDao被赋值:com.xiaoxie.dao.impl.TestDIDaoImpl@38e79ae3
调用setter方法
setter方法调用完成,testDIDao1对象:com.xiaoxie.dao.impl.TestDIDaoImpl@38e79ae3
sayHello()方法输出:DI注入类型-构造方法
--------setter方法进行注入-------
sayHello()方法输出:DI注入类型-setter方法
从上面打印的结果可以看到,在使用Spring配置文件初始化Spring容器时Spring就会把依赖全部做完,且它默认是一个单例的方式进行的注入