Spring框架的主要内容
1.Spring框架的主要内容:
简化开发: Spring框架中提供了两个大的核心技术,分别是:
- ==IOC==
- ==AOP==
1.1系统架构图

(1)核心层
- Core Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块
(2)AOP层
- AOP:面向切面编程,它依赖核心层容器,目的是==在不改变原有代码的前提下对其进行功能增强==
- Aspects:AOP是思想,Aspects是对AOP思想的具体实现
(3)数据层
- Data Access:数据访问,Spring全家桶中有对数据访问的具体实现技术
- Data Integration:数据集成,Spring支持整合其他的数据层解决方案,比如Mybatis
- Transactions:事务,Spring中事务管理是Spring AOP的一个具体实现,也是后期学习的重点内容
(4)Web层
- 这一层的内容将在SpringMVC框架具体学习
(5)Test层
- Spring主要整合了Junit来完成单元测试和集成测试
1.2学习路线
- ==Spring的IOC/DI==
- ==Spring的AOP==
- ==AOP的具体应用,事务管理==
- ==IOC/DI的具体应用,整合Mybatis==

2.Spring核心概念
在Spring核心概念这部分内容中主要包含**IOC/DI、IOC容器和Bean**
2.1先来说说为什么要用这些,我们之前项目中的痛点

(1)业务层需要调用数据层的方法,就需要在业务层new数据层的对象
(2)如果数据层的实现类发生变化,那么业务层的代码也需要跟着改变,发生变更后,都需要进行编译打包和重部署
(3)所以,现在代码在编写的过程中存在的问题是:**==耦合度偏高==**
针对这个问题,该如何解决呢?

我们就想,如果能把框中的内容给去掉,不就可以降低依赖了么,但是又会引入新的问题,去掉以后程序能运行么?
答案肯定是不行,因为bookDao没有赋值为Null,强行运行就会出空指针异常。
所以现在的问题就是,业务层不想new对象,运行的时候又需要这个对象,该咋办呢?
针对这个问题,Spring就提出了一个解决方案:
- 使用对象时,在程序中不要主动使用new产生对象,转换为由==外部==提供对象
这种实现思就是Spring的一个核心概念
2.IOC、IOC容器、Bean、DI
2.1IOC(Inversion of Control)控制反转
使用对象时,由主动new产生对象转换为由==外部==提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。
1.外部:
- 业务层u要用数据层的类对象,以前是自己
new的 - 现在自己不new了,交给
别人[外部]来创建对象 别人[外部]就反转控制了数据层对象的创建权- 这种思想就是控制反转
- 别人[外部]指定是什么呢?继续往下学
2.Spring和IOC之间的关系是什么呢?
- Spring技术对IOC思想进行了实现
- Spring提供了一个容器,称为==IOC容器==,用来充当IOC思想中的”外部”
- IOC思想中的
别人[外部]指的就是Spring的IOC容器
3.IOC容器的作用以及内部存放的是什么?(什么是bean)
- IOC容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象
- 被创建或被管理的对象在IOC容器中统称为==Bean==
- IOC容器中放的就是一个个的Bean对象
4.当IOC容器中创建好service和dao对象后,程序能正确执行么?
- 不行,因为service运行需要依赖dao对象
- IOC容器中虽然有service和dao对象
- 但是service对象和dao对象没有任何关系
- 需要把dao对象交给service,也就是说要绑定service和dao对象之间的关系
像这种在容器中建立对象与对象之间的绑定关系就要用到DI:
2.DI(Dependency Injection)依赖注入
2.1什么是依赖注入呢?(关系)
- 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
- 业务层要用数据层的类对象,以前是自己
new的 - 现在自己不new了,靠
别人[外部其实指的就是IOC容器]来给注入进来 - 这种思想就是依赖注入
- 业务层要用数据层的类对象,以前是自己

2.IOC容器中哪些bean之间要建立依赖关系呢?
- 这个需要程序员根据业务需求提前建立好关系,如业务层需要依赖数据层,service就要和dao建立依赖关系
2.2核心概念:解耦
目标:充分解耦
- 使用IoC容器管理bean(IoC)
- 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
最终效果:
- 使用对象时不仅可以直接IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系。
2.3核心概念小结
1.什么IOC/DI思想?
- IOC:控制反转,控制反转的是对象的创建权
- DI:依赖注入,绑定对象与对象之间的依赖关系
2.什么是IOC容器?
Spring创建了一个容器用来存放所创建的对象,这个容器就叫IOC容器
3.什么是Bean?
容器中所存放的一个个对象就叫Bean或Bean对象
3.入门案例(了解步骤即可)
1.Spring是使用容器来管理bean对象的,那么管什么? (Service和Dao)
2.如何将被管理的对象告知IOC容器?(使用配置文件)
3.被管理的对象交给IOC容器,要想从容器中获取对象,就先得思考如何获取到IOC容器?(Spring框架提供相应的接口)
4.IOC容器得到后,如何从容器中获取bean?(调用Spring框架提供对应接口中的方法)
5.使用Spring导入哪些坐标?(需要在pom.xml添加对应的依赖)

步骤1:创建Maven项目

步骤2:添加Spring的依赖jar包
pom.xml
1 | <dependencies> |
步骤3:添加案例中需要的类
创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类
1 | public interface BookDao { |
步骤4:添加spring配置文件
resources下添加spring配置文件applicationContext.xml,并完成bean的配置

步骤5:在配置文件中完成bean的配置
1 |
|
==注意事项:bean定义时id属性在同一个上下文中(配置文件)不能重复==
步骤6:获取IOC容器
使用Spring提供的接口完成IOC容器的创建,创建App类,编写main方法
1 | public class App { |
步骤7:从容器中获取对象进行方法调用
1 | public class App { |
步骤8:运行程序
测试结果为:

Spring的IOC入门案例已经完成,但是在BookServiceImpl的类中依然存在BookDaoImpl对象的new操作,它们之间的耦合度还是比较高,这块该如何解决,就需要用到下面的DI:依赖注入。
4.DI入门案例(上个案例基础)
4.1思路分析
(1)要想实现依赖注入,必须要基于IOC管理Bean
- DI的入门案例要依赖于前面IOC的入门案例
2.Service中使用new形式创建的Dao对象是否保留?
- 需要删除掉,最终要使用IOC容器中的bean对象
3.Service中需要的Dao对象如何进入到Service中?
- 在Service中提供方法,让Spring的IOC容器可以通过该方法传入bean对象
4.Service与Dao间的关系如何描述?
- 使用配置文件

步骤1: 去除代码中的new
在BookServiceImpl类中,删除业务层中使用new的方式创建的dao对象
1 | public class BookServiceImpl implements BookService { |
步骤2:为属性提供setter方法
在BookServiceImpl类中,为BookDao提供setter方法
1 | public class BookServiceImpl implements BookService { |
步骤3:修改配置完成注入
在配置文件中添加依赖注入的配置
1 |
|
==注意:配置中的两个bookDao的含义是不一样的==
- name=”bookDao”中
bookDao的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前面加set找对应的setBookDao()方法进行对象注入 - ref=”bookDao”中
bookDao的作用是让Spring能在IOC容器中找到id为bookDao的Bean对象给bookService进行注入 - 综上所述,对应关系如下:

步骤4:运行程序
运行,测试结果为:

5.IOC相关内容
5.1 bean基础配置(bean的作用范围配置)
对于bean的配置中,主要会讲解bean基础配置,bean的别名配置,bean的作用范围配置==(重点)==,这三部分内容:
5.1.1 bean基础配置(id与class)
对于bean的基础配置,在前面的案例中已经使用过:
1 | <bean id="" class=""/> |
其中,bean标签的功能、使用方式以及id和class属性的作用,我们通过一张图来描述下

这其中需要大家重点掌握的是:==bean标签的id和class属性的使用==。
思考:
- class属性能不能写接口如
BookDao的类全名呢?
答案肯定是不行,因为接口是没办法创建对象的。
5.1.2 bean作用范围scope配置
关于bean的作用范围是bean属性配置的一个==重点==内容。
看到这个作用范围,我们就得思考bean的作用范围是来控制bean哪块内容的?
我们先来看下bean作用范围的配置属性:

验证IOC容器中对象是否为单例
验证思路
同一个bean获取两次,将对象打印到控制台,看打印出的地址值是否一致。
具体实现
创建一个AppForScope的类,在其main方法中来验证
1
2
3
4
5
6
7
8
9
10
11public class AppForScope {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
BookDao bookDao2 = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao1);
System.out.println(bookDao2);
}
}打印,观察控制台的打印结果

结论:默认情况下,Spring创建的bean对象都是单例的

哪些bean对象不适合交给容器进行管理?
- 封装实例的域对象,因为会引发线程安全问题,所以不适合。
获取到结论后,问题就来了,那如果我想创建出来非单例的bean对象,该如何实现呢?
bean的scope属性可以控制bean的创建是否为单例:(重要)
singleton默认为单例prototype为非单例

1 | scope="singleton"/> |
5.2 bean实例化(重要 )
bean本质上就是对象,创建bean使用构造方法完成。
实例化bean的三种方式————构造方法(常用)

静态工厂实例化(了解)
具体实现步骤为:
(1)在spring的配置文件application.properties中添加以下内容:
1 | <bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/> |
class:工厂类的类全名
factory-mehod:具体工厂类中创建对象的方法名
对应关系如下图:

(2)在AppForInstanceOrder运行类,使用从IOC容器中获取bean的方法进行运行测试
1 | public class AppForInstanceOrder { |
(3)运行后,可以查看到结果
看到这,可能有人会问了,你这种方式在工厂类中不也是直接new对象的,和我自己直接new没什么太大的区别,而且静态工厂的方式反而更复杂,这种方式的意义是什么?
主要的原因是:
- 在工厂的静态方法中,我们除了new对象还可以做其他的一些业务操作,这些操作必不可少,如:
1 | public class OrderDaoFactory { |
3.实例工厂(了解)与FactoryBean(知道)
实例工厂实例化
具体实现步骤为:
(1)在spring的配置文件中添加以下内容:
1 | <bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/> |
实例化工厂运行的顺序是:
创建实例化工厂对象,对应的是第一行配置
调用对象中的方法来创建bean,对应的是第二行配置
factory-bean:工厂的实例对象
factory-method:工厂对象中的具体创建对象的方法名,对应关系如下:

factory-mehod:具体工厂类中创建对象的方法名
(2)在AppForInstanceUser运行类,使用从IOC容器中获取bean的方法进行运行测试
1 | public class AppForInstanceUser { |
(3)运行后,可以查看到结果
实例工厂实例化的方式就已经介绍完了,配置的过程还是比较复杂,所以Spring为了简化这种配置方式就提供了一种叫FactoryBean的方式来简化开发。
FactoryBean的使用
具体的使用步骤为:
(1)创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法
1 | public class UserDaoFactoryBean implements FactoryBean<UserDao> { |
(2)在Spring的配置文件中进行配置
1 | <bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/> |
(3)AppForInstanceUser运行类不用做任何修改,直接运行

这种方式在Spring去整合其他框架的时候会被用到,所以这种方式需要大家理解掌握。
总结
(1)bean是如何创建的呢?
1 | 构造方法 |
(2)Spring的IOC实例化对象的三种方式分别是:
- 构造方法(常用)
- 静态工厂(了解)
- 实例工厂(了解)
- FactoryBean(实用)
这些方式中,重点掌握构造方法和FactoryBean即可。
需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在使用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下。
5.3 bean的生命周期
- 首先理解下什么是生命周期?
- 从创建到消亡的完整过程,例如人从出生到死亡的整个过程就是一个生命周期。
- bean生命周期是什么?
- bean对象从创建到销毁的整体过程。
- bean生命周期控制是什么?
- 在bean创建后到销毁前做一些事情。
5.3.1 生命周期设置
接下来,在上面这个环境中来为BookDao添加生命周期的控制方法,具体的控制有两个阶段:
- bean创建之后,想要添加内容,比如用来初始化需要用到资源
- bean销毁之前,想要添加内容,比如用来释放用到的资源
步骤1:添加初始化和销毁方法
针对这两个阶段,我们在BooDaoImpl类中分别添加两个方法,==方法名任意==
1 | public class BookDaoImpl implements BookDao { |
步骤2:配置生命周期
在配置文件添加配置,如下:
1 | <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/> |
步骤3:运行程序
运行AppForLifeCycle打印结果为:

从结果中可以看出,init方法执行了,但是destroy方法却未执行,这是为什么呢?
- Spring的IOC容器是运行在JVM中
- 运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行
- main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了
- 所以没有调用对应的destroy方法
4.3.3 close关闭容器
ApplicationContext中没有close方法
需要将ApplicationContext更换成ClassPathXmlApplicationContext
1
2ClassPathXmlApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");调用ctx的close()方法
1
ctx.close();
运行程序,就能执行destroy方法的内容

4.3.4 注册钩子关闭容器
在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器
调用ctx的registerShutdownHook()方法
1
ctx.registerShutdownHook();
**注意:**registerShutdownHook在ApplicationContext中也没有
运行后,查询打印结果

两种方式介绍完后,close和registerShutdownHook选哪个?
相同点:这两种都能用来关闭容器
不同点:close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭。
分析上面的实现过程,会发现添加初始化和销毁方法,即需要编码也需要配置,实现起来步骤比较多也比较乱。
Spring提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置init-method和destroy-method
接下来在BookServiceImpl完成这两个接口的使用:
修改BookServiceImpl类,添加两个接口InitializingBean, DisposableBean并实现接口中的两个方法afterPropertiesSet和destroy
1 | public class BookServiceImpl implements BookService, InitializingBean, DisposableBean { |
重新运行AppForLifeCycle类,

那第二种方式的实现,我们也介绍完了。
小细节
对于InitializingBean接口中的afterPropertiesSet方法,翻译过来为
属性设置之后。对于BookServiceImpl来说,bookDao是它的一个属性
setBookDao方法是Spring的IOC容器为其注入属性的方法
思考:afterPropertiesSet和setBookDao谁先执行?
从方法名分析,猜想应该是setBookDao方法先执行
验证思路,在setBookDao方法中添加一句话
1
2
3
4
5public void setBookDao(BookDao bookDao) {
System.out.println("set .....");
this.bookDao = bookDao;
}重新运行AppForLifeCycle,打印结果如下:

验证的结果和我们猜想的结果是一致的,所以初始化方法会在类中属性设置之后执行。
4.3.5 bean生命周期小结
(1)关于Spring中对bean生命周期控制提供了两种方式:
- 在配置文件中的bean标签中添加
init-method和destroy-method属性 - 类实现
InitializingBean与DisposableBean接口,这种方式了解下即可。
(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
- 初始化容器
- 1.创建对象(内存分配)
- 2.执行构造方法
- 3.执行属性注入(set操作)
- 4.执行bean初始化方法
- 使用bean
- 执行业务操作
- 关闭/销毁容器
- 执行bean销毁方法
(3)关闭容器的两种方式:
- ConfigurableApplicationContext是ApplicationContext的子类
- close()方法
- registerShutdownHook()方法




