
00 SSM框架 学习
Spring
Spring 介绍
- 官网:https://spring.io
- Spring Framework:Spring框架,是Spring中最早最核心的技术,也是所有其他技术的基础
- SpringBoot:Spring是来简化开发,而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发
- SpringCloud:这个是用来做分布式之微服务架构的相关开发
- 还有:SpringData 、SpringSecurity 等等
Spring 的发展史:
- 随着时间推移,版本不断更新维护,目前最新的是Spring5
- Spring1.0 是 纯配置文件 开发
- Spring2.0 为了简化开发引入了 注解 开发,此时是配置文件加注解的开发方式
- Spring3.0 已经可以进行 纯注解 开发,使开发效率大幅提升
- Spring4.0 根据 JDK的版本升级 对 个别API 进行了调整
- Spring5.0 已经 全面支持JDK8,现在Spring最新的是5系列所以建议把JDK安装成1.8版
Spring 系统架构
Spring Framework 是Spring生态圈中最基础的项目,是其他项目的根基

因为 Spring Framework 的 5 版本目前没有最新的架构图,主要研究的是 4 的架构图

- Core container(核心层)
- 核心容器,这个模块是 Spring 最核心的模块,其他的都需要依赖该模块
- AOP层
- AOP:面向切面编程,它依赖核心层容器,目的是 在不改变原有代码的前提下对其进行功能增强
- Aspects:AOP是思想,Aspects是对AOP思想的具体实现
- 数据层
- Data Access:数据访问,Spring全家桶 中有对数据访问的具体实现技术
- Data Integration:数据集成,Spring支持整合其他的数据层解决方案,比如:Mybatis
- Transactions:事务,Spring中事务管理是Spring AOP的一个具体实现
- Web层
- 主要指 SpringMVC框架
- Test层
- Spring主要整合了 Junit 来完成 单元测试 和 集成测试
IoC(Inversion of Control)
IoC(Inversion of Control):控制反转,使用对象时,由主动 new 产生对象转换为由 外部 提供对象,此过程中 对象创建控制权由 程序 转移到 外部,此思想称为控制反转
- 控制反转 的含义:
- 业务层要用数据层的类对象,以前是自己 new 的
- 现在自己不 new 了,交给别人 [外部] 来创建对象
- 别人 [外部] 就反转控制了数据层对象的创建权
- IOC思想中的别人 [外部] 指的就是 Spring 的 IOC容器
Spring技术 对 IoC思想 进行了实现:
-
Spring提供了一个容器,称为 IoC容器,用来充当IoC思想中的"外部",IoC容器 是 Spring创建了一个容器用来存放所创建的对象

-
IoC容器 负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象,被创建或被管理的对象在IoC容器中统称为 Bean
- IoC容器 中放的就是一个个的 Bean对象,也就是说 IoC容器 管理的对象叫 Bean 对象
IoC 入门案例分析(XML版本)
- Spring是使用容器来管理bean对象的,主要管理项目中所使用到的类对象,比如:(Service和Dao)
- 将被管理的对象告知IoC容器的方式:使用配置文件
- 被管理的对象交给IoC容器,要想从容器中获取对象,通过 Spring框架提供相应的接口 获取到IoC文件
- IoC容器得到后,通过 调用Spring框架提供对应接口中的方法 从容器中获取bean
- 使用Spring导入坐标:用别人的东西,就需要 在 pom.xml 添加对应的依赖
流程:
-
添加Spring的依赖 jar 包,pom.xml,这步就是导入 Spring 坐标
1
2
3
4
5
6
7
8
9
10
11
12
13<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies> -
创建 BookService、BookServiceImpl 、BookDao 和 BookDaoImpl 四个类
-
添加 Spring配置文件 applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<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">
<!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!--2.配置bean-->
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
</beans>- bean定义时 id 属性在 同一个上下文中 (配置文件)不能重复
-
获取 IOC 容器 和 获取 Bean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class App {
public static void main(String[] args) {
// 3.获取IoC容器
// ApplicationContext 要与配置文件名相同,并且这个一个接口
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 4.获取bean(根据bean配置id获取)
// BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// bookDao.save();
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
DI(Dependency Injection)
DI(Dependency Injection):依赖注入,在容器中建立 bean 与 bean 之间的依赖关系的整个过程
- 依赖注入 的含义:
- 业务层要用数据层的类对象,以前是自己 new 的
- 现在自己不 new 了,靠别人 [外部其实指的就是IOC容器] 来给注入进来
IoC 和 DI 的目标:充分解耦
- 使用IoC容器管理 bean(IOC)
- 在IoC容器内将 有依赖关系的bean 进行关系绑定(DI)
- 最终结果为:使用对象时 不仅可以直接从 IoC容器 中获取,并且获取到的 bean 已经绑定了所有的依赖关系
DI 入门案例分析(XML版本)
- 基于 IoC 管理 bean
- 不再保留 Service 中使用 new 形式创建的 Dao 对象
- 提供一个方法使得 Service 中需要的 Dao 对象传入 Service 中
- 通过 配置 来建立 Service 与 Dao 之间的关系
流程:
-
删除代码中的 new
1
2
3
4
5
6
7
8
9
10public class BookServiceImpl implements BookService {
// 删除业务层中使用new的方式创建的dao对象
// private BookDao bookDao = new BookDaoImpl();
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
} -
提供依赖对象对应的 setter 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class BookServiceImpl implements BookService {
//删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
} -
配置 service 与 dao 之间的关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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">
<!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!--2.配置bean-->
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--7.配置service与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
-
name=“bookDao” 中 bookDao 的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前
面加 set 找对应的 setBookDao() 方法进行对象注入
-
ref=“bookDao” 中 bookDao 的作用是让 Spring 能在IOC容器中找到 id 为 bookDao 的Bean对象给
bookService 进行注入
-
依赖注入 的两种方式
setter 注入
- 简单类型(基本数据类型 与 String)
- 引用类型
构造器 注入
- 简单类型(基本数据类型 与 String)
- 引用类型
setter 注入
-
在 bean 中定义 引用类型属性,并提供可访问的 set 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class BookServiceImpl implements BookService {
// 注入引用类型
private BookDao bookDao;
// 注入简单类型(String)
private String databaseName;
// 注入简单类型(基本数据类型)
private int connectionNum;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
} -
配置中使用 property 标签 ref 属性注入 引用类型对象
1
2
3
4
5<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.itheima.dao.imipl.BookDaoImpl"/> -
如果是注入简单数据类型
1
2
3
4
5
6
7<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="databaseName" value="mysql"/>
<property name="connectionNum" value="10"/>
</bean>
<bean id="bookDao" class="com.itheima.dao.imipl.BookDaoImpl"/>
总结:
- 对于 引用数据类型 使用的是
<property name="" ref=""/> - 对于 简单数据类型 使用的是
<property name="" value=""/>
构造器 注入
构造器注入 就是 构造方法注入,如果是简单数据类型,同理
流程:
-
项目中添加BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService 和 BookServiceImpl 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
} -
resources 下提供 spring 的配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<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="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
根据构造方法参数名称注入
<constructor-arg name="connectionNum" value="10"/>
<constructor-arg name="databaseName" value="mysql"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
-->
<!--
解决形参名称的问题,与形参名不耦合
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
根据 构造方法 参数类型 注入
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>-->
<!--解决参数类型重复问题,使用 位置 解决参数匹配-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--根据构造方法参数位置注入-->
<!-- index 表明参数的位置 -->
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="100"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
</beans> -
编写 AppForDIConstructor 运行类,加载 Spring 的 IoC 容器,并从中获取对应的 bean 对象
1
2
3
4
5
6
7public class AppForDIConstructor {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
总结:
-
简单数据类型
1
2
3<bean ...>
<constructor-arg name="" index="" type="" value=""/>
</bean> -
引用数据类型
1
2
3<bean ...>
<constructor-arg name="" index="" type="" ref=""/>
</bean>
依赖注入的方式选择上:
- 建议使用 setter 注入
- 第三方技术根据情况选择
依赖 自动装配
IoC容器 根据 bean 所依赖的资源 在容器中自动查找 并注入到 bean 中的过程称为 自动装配
自动装配方式:
- 按 类型(常用)
- 按 名称
- 按 构造方法
- 不启用自动装配
流程:
自动装配 需要修改 applicationContext.xml 配置文件:
- 将
<property>标签删除 - 在
<bean>标签中添加 autowire 属性
eg:
1 |
|
注意:
-
需要 注入属性的类中对应属性 的 setter 方法不能省略
-
被注入的对象必须要被 Spring 的 IOC容器 管理
-
按照类型在 Spring 的 IOC容器 中 如果找到 多个对象,会报
NoUniqueBeanDefinitionException -
自动装配 用于 引用类型依赖 注入,不能对 简单类型 进行操作
-
使用按 类型 装配时(byType)必须保障容器中相同类型的 bean 唯一,推荐使用
-
使用按 名称 装配时(byName)必须保障 容器 中具有 指定名称 的 bean,因变量名与配置耦合,不推荐使用
1
2
3<bean id = "bookDao" class = "com.itheima.dao.impl.BaoDaoImpl">
<bean id = "bookService" class = "com.itheima.service.impl.BookServiceImpl" autowire = "byName"> -
自动装配优先级 低于 setter 注入 与 构造器注入,同时出现时自动装配配置失效
集合 注入
常见的集合类型:
- 数组
- List
- Set
- Map
- Properties
流程:
在 bookDao 的 bean 标签中 使用进行注入
1 | <!-- 数组注入 --> |
1 | <!-- List注入 --> |
1 | <!-- set注入 --> |
1 | <!-- map注入 --> |
1 | <!-- properties注入 --> |
第三方 依赖注入
流程:
-
pom.xml 添加依赖
1
2
3
4
5<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency> -
在 applicationContext.xml 配置文件中添加
DruidDataSource的配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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">
<!--管理DruidDataSource对象-->
<bean class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>- driverClassName:数据库驱动
- url:数据库连接地址
- username:数据库连接用户名
- password:数据库连接密码
- 数据库连接的 四要素 要和 自己使用的数据库 信息一致
- 注入数据库连接四要素 的方式:setter 注入
-
从 IoC容器 中获取对应的 bean 对象
1
2
3
4
5
6
7public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
bean
bean 的基础配置

- 重点:bean 标签的 id 和 class 属性的使用
bean 的别名配置

- 获取 bean 无论是通过 id 还是 name 获取,如果无法获取到,将抛出异常 NoSuchBeanDefinitionException
bean 作用范围 scope 配置

- 主要就是控制 bean 创建的数量,是一个还是多个,bean 默认是 singleton(单例模式)
- 适合交给 IoC 容器进行管理的 bean:
- 表现层对象
- 业务层对象
- 数据层对象
- 工具对象
- 不适合交给 IoC 容器进行管理的 bean:
- 封装实体的域对象
bean 的实例化
bean 本质上就是对象,对象在 new 的时候会使用构造方法完成,那创建 bean 也是使用构造方法完成的
方式1:使用构造方法(常用)
-
提供可以访问的 构造方法
1
2
3
4
5
6
7
8
9
10public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book constructor is running ...");
}
public void save() {
System.out.println("book dao save ...");
}
} -
配置
1
<bean id = "bookDao" class = "com.itheima.dao.impl.bookDaoImpl"/>
- 注意:无参构造方法如果不存在,会抛出异常
BeanCreationException
- 注意:无参构造方法如果不存在,会抛出异常
方式2:使用静态工厂
-
准备一个 OrderDao 和 OrderDaoImpl 类
1
2
3
4
5
6
7
8
9public interface OrderDao {
public void save();
}
public class OrderDaoImpl implements OrderDao {
public void save() {
System.out.println("order dao save ...");
}
} -
创建一个工厂类 OrderDaoFactory 并提供一个 静态方法
1
2
3
4
5
6//静态工厂创建对象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
} -
编写 AppForInstanceOrder 运行类,在类中通过工厂获取对象
1
2
3
4
5
6
7public class AppForInstanceOrder {
public static void main(String[] args) {
//通过静态工厂创建对象
OrderDao orderDao = OrderDaoFactory.getOrderDao();
orderDao.save();
}
} -
配置
1
2<bean id = "orderDao" class = "com.itheima.factory.OrderDaoFactory"
factory-method = "getOrderDao"/>- factory-method:具体工厂类中创建对象的方法名
-
在 AppForInstanceOrder 运行类,使用从 IoC容器 中获取 bean的方法 进行运行测试
1
2
3
4
5
6
7
8
9
10public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
orderDao.save();
}
}
方式3:使用实例工厂
-
准备一个 UserDao 和 UserDaoImpl 类
1
2
3
4
5
6
7
8
9public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
} -
创建一个工厂类 OrderDaoFactory 并 提供一个普通方法,注意此处和静态工厂的工厂类不一样的地方是方法不是静态方法
1
2
3
4
5public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
} -
编写 AppForInstanceUser 运行类,在类中通过工厂获取对象
1
2
3
4
5
6
7
8
9public class AppForInstanceUser {
public static void main(String[] args) {
//创建实例工厂对象
UserDaoFactory userDaoFactory = new UserDaoFactory();
//通过实例工厂对象创建对象
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();
} -
在spring的配置文件中 添加配置
1
2
3
4
5<!-- 先造出来工厂对象 -->
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<!-- 使用工厂对象 -->
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
方式4:使用FactoryBean
流程:
-
创建一个 UserDaoFactoryBean 的类,实现 FactoryBean 接口,重写接口的方法
1
2
3
4
5
6
7
8
9
10
11
12public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
//返回所创建类的Class对象
public Class<?> getObjectType() {
return UserDao.class;
}
} -
在Spring的配置文件中进行 配置
1
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
FactoryBean接口其实会有三个方法,分别是:
1 | T getObject() throws Exception; |
- 方法一:
getObject(),被重写后,在方法中进行 对象的创建 并 返回 - 方法二::
getObjectType(),被重写后,主要返回的是 被创建类的Class对象 - 方法三:从容器中 获取该对象的多个值,打印到控制台,查看是否为同一个对象
- 默认是 单例,如果要修改,将
isSingleton()方法进行重写,修改返回为 false
- 默认是 单例,如果要修改,将
bean 的生命周期
指的是:bean对象 从创建到销毁 的整体过程
bean 生命周期控制:指的是在 bean 创建后到销毁前 做一些事情
生命周期的设置:
- bean 创建之后,想要添加内容,比如:用来初始化需要用到资源
- bean 销毁之前,想要添加内容,比如:用来释放用到的资源
流程:
-
添加初始化和销毁方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init() {
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destroy() {
System.out.println("destory...");
}
} -
配置 生命周期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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">
<!--init-method:设置bean初始化生命周期回调函数-->
<!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
从结果中可以看出,init 方法执行了,但是 destroy 方法却未执行,原因是:
- Spring 的 IoC 容器是运行在 JVM 中
- 运行 main 方法后,JVM 启动,Spring加载配置文件生成IoC容器,从容器获取 bean 对象,然后调方法执行
- main 方法执行完后,JVM退出,这个时候 IoC 容器中的 bean 还没有来得及销毁就已经结束了
因为 ApplicationContext 中没有 close 方法,需要将 ApplicationContext 更换成 ClassPathXmlApplicationContext,运行程序,就能执行 destroy 方法的内容
1 | public class AppForLifeCycle { |
-
在容器未关闭之前,提前设置好回调函数,让 JVM 在退出之前回调此函数来关闭容器,调用ctx的
registerShutdownHook()方法1
ctx.registerShutdownHook();
close() 和 registerShutdownHook() 的异同:
- 相同点:这两种都能用来关闭容器
- 不同点:close() 是在 调用的时候 关闭,registerShutdownHook() 是在 JVM退出前 调用关闭
上面的流程需要手动添加 初始化 和 销毁方法,即需要编码也需要配置,十分繁琐,Spring提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置 init-method 和 destroy-method
修改BookServiceImpl类,添加两个接口 InitializingBean, DisposableBean 并实现接口中的两个方法 afterPropertiesSet 和 destroy
1 | public class BookDaoImpl implements BookService, InitializingBean, DisposableBean { |
- 对于 InitializingBean 接口中的 afterPropertiesSet 方法,翻译过来为属性设置之后
- 对于 BookServiceImpl 来说,bookDao 是它的一个属性
- setBookDao 方法是 Spring 的 IOC容器 为其 注入属性 的方法
对于 bean的生命周期 控制在 bean 的整个生命周期 中所处的位置如下:
-
初始化容器
-
创建对象(内存分配)
-
执行构造方法
-
执行属性注入(set操作)
-
执行 bean 初始化方法
-
-
使用bean
- 执行业务操作
-
关闭/销毁容器
- 执行 bean 销毁方法
关闭容器 的 两种方式:
在 ConfigurableApplicationContext 是 ApplicationContext 的子类
- 手动关闭容器的方法:close() 方法
- 注册关闭钩子,在虚拟机退出前先关闭容器再关闭虚拟机:registerShutdownHook() 方法
Spring 中加载 properties 文件
流程:
-
准备 .properties 配置文件
1
2
3
4jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root -
开启
context命名空间,在 applicationContext.xml 中开context命名空间1
2
3
4
5
6
7
8
9
10
<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> -
加载 properties 配置文件
- 在配置文件中使用
context命名空间下的 标签 来加载 properties 配置文件
1
<context:property-placeholder location="jdbc.properties"/>
- 在配置文件中使用
-
完成属性注入
- 使用
${key}来读取 properties 配置文件中的内容并完成属性注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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">
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans> - 使用
上面流程的优化:
1 |
|
system-properties-mode:设置为 NEVER ,表示 不加载系统属性
说明:
- 方式一:可以实现,如果配置文件多的话,每个都需要配置
- 方式二:
*.properties代表所有以 properties 结尾的文件都会被加载,可以解决方式一的问题,但是不标准 - 方式三:标准的写法,
classpath:代表的是从 根路径下 开始查找,但是只能查询当前项目的根路径 - 方式四:不仅可以加载 当前项目 还可以加载 当前项目所依赖的所有项目 的根路径下的 properties 配置文件
总结:
-
开启
context命名空间的方法:
-
加载 properties 配置文件的方法:
1
<context:property-placeholder location="" system-properties-mode="NEVER"/>
-
在 applicationContext.xml 引入 properties 配置文件中的值的方法:
1
${key}
容器
容器的创建方式
方式1:加载类路径下的 XML配置文件
1 | ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); |
方式2:加载文件系统下的 XML配置文件
1 | ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\...\\applicationContext.xml"); |
方式3:加载多个配置文件
1 | ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xml"); |
Bean 的 三种获取方式
方式1:
1 | BookDao bookDao = (BookDao) ctx.getBean("bookDao"); |
- 存在的问题是:每次获取的时候 都需要进行 类型转换
方式2:
1 | BookDao bookDao = ctx.getBean("bookDao",BookDao.class); |
- 这种方式可以解决类型强转问题,但是参数又多加了一个,相对来说没有简化多少
方式3:
1 | BookDao bookDao = ctx.getBean(BookDao.class); |
- 这种方式类似于 依赖注入中 的 按类型注入,但是必须要确保 IOC容器 中该类型对应的 bean 对象只能有一个
BeanFactory
BeanFactory 是 ApplicationContext 的 顶层接口

流程:
使用 BeanFactory 来创建 IOC容器 的具体实现方式为:
1 | public class AppForBeanFactory { |
BeanFactory 与 ApplicationContext 的区别:
-
BeanFactory 是 延迟加载 ,只有在获取 bean对象 的时候才会去创建
-
ApplicationContext 是 立即加载 ,容器加载的时候就会创建bean对象
-
ApplicationContext 要想 成为延迟加载,只需要按照如下方式进行配置:
1
2
3
4
5
6
7
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="bookDao" class="com.itheima.dao.impl.BookDaoImpl" lazy-init="true"/>
</beans>
-
核心容器 相关总结
容器相关:
- BeanFactory是 IoC 容器的 顶层接口,初始化BeanFactory对象时,加载的bean 延迟加载
- ApplicationContext 接口是 Spring容器 的 核心接口,初始化时bean 立即加载
- ApplicationContext 接口提供 基础的bean操作 相关方法,通过其他接口扩展其功能
- ApplicationContext接口 常用初始化类
- ClassPathXmlApplicationContext(常用)
- FileSystemXmlApplicationContext
bean相关:

- 最常用的:id 和 class
依赖注入相关:

注解开发
Spring对注解支持的版本历程:
- 2.0版 开始支持注解
- 2.5版 注解功能趋于完善
- 3.0版 支持 纯注解开发
注解开发定义bean
流程:
-
删除原XML配置
- 将配置文件中的
<bean>标签删除掉
1
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
- 将配置文件中的
-
Dao上添加注解
- 在BookDaoImpl类上添加
@Component注解
1
2
3
4
5
6
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ..." );
}
}
- 在BookDaoImpl类上添加
-
配置 Spring 的注解包扫描
1
2
3
4
5
6
7
<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">
<context:component-scan base-package="com.itheima"/>
</beans>说明:
-
component-scan
-
component:组件,Spring将管理的 bean 视作自己的一个组件
-
scan:扫描
-
-
base-package 指定 Spring 框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
-
包路径越多(如:com.itheima.dao.impl),扫描的范围越小速度越快
-
包路径越少(如:com.itheima),扫描的范围越大速度越慢
-
一般扫描到项目的组织名称即Maven的 groupId 下(如:com.itheima)即可
-
-
-
Service 上添加注解
- 在 BookServiceImpl 类上也添加
@Component交给Spring框架管理
1
2
3
4
5
6
7
8
9
10
11
12
13
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}- @Component注解如果不起名称,会有一个默认值就是
当前类名首字母小写
- 在 BookServiceImpl 类上也添加
对于 @Component 注解,还衍生出了其他三个注解@Controller、@Service、@Repository
这三个注解和 @Component 注解的作用是一样的,分别是属于表现层、业务层、数据层的类
纯注解开发 模式
实现思路:将配置文件 applicationContext.xml 删除掉,使用类来替换
流程:
-
创建一个配置类
SpringConfig1
2public class SpringConfig {
} -
标识该类为 配置类
- 在配置类上添加
@Configuration注解,将其标识为一个配置类,替换applicationContext.xml
1
2
3
public class SpringConfig {
} - 在配置类上添加
-
用 注解替换包 扫描配置
- 在配置类上添加包扫描注解
@ComponentScan替换<context:component-scan base-package=""/>
1
2
3
4
public class SpringConfig {
} - 在配置类上添加包扫描注解
-
创建 运行类 并执行
1
2
3
4
5
6
7
8
9
10
11
12
13public class AppForAnnotation {
public static void main(String[] args) {
// 这里是加载配置类
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
BookService bookService = ctx.getBean(BookService.class);
System.out.println(bookService);
}
}
注意:
-
@Configuration 注解用于设定当前类为配置类
-
@ComponentScan 注解用于设定扫描路径,此注解只能添加一次,多个数据请用 数组 格式
1
-
读取Spring核心配置文件 初始化容器对象 切换为读取 Java配置类初始化容器对象
1
2
3
4
5//加载配置文件初始化容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
bean 的 作用范围
想将 BookDaoImpl 变成 非单例,只需要在其类上添加 @scope 注解
1 |
|
| 名称 | @Scope |
|---|---|
| 类型 | 类注解 |
| 位置 | 类定义上方 |
| 作用 | 设置该类创建对象的作用范围 可用于设置创建出的bean是否为单例对象 |
| 属性 | value(默认):定义bean作用范围, 默认值singleton(单例),可选值prototype(非单例) |
bean 的 生命周期
| 名称 | @PostConstruct |
|---|---|
| 类型 | 方法注解 |
| 位置 | 方法上 |
| 作用 | 设置该方法为初始化方法 |
| 属性 | 无 |
| 名称 | @PreDestroy |
|---|---|
| 类型 | 方法注解 |
| 位置 | 方法上 |
| 作用 | 设置该方法为销毁方法 |
| 属性 | 无 |
对方法进行标识,判断 初始化方法 还是 销毁方法:需要在对应的方法上添加 @PostConstruct 和 @PreDestroy 注解即可,destroy只有在 容器关闭 的时候,才会执行
1 |
|
@PostConstruct 和 @PreDestroy 注解如果找不到,需要导入下面的 jar 包
1 | <dependency> |
总结:

注解开发 依赖注入
注解实现 引用类型 注入
Spring为了使用注解简化开发,并没有提供构造函数注入、setter注入对应的注解,只提供了自动装配的注解实现 @Autowired
| 名称 | @Autowired |
|---|---|
| 类型 | 属性注解 或 方法注解(了解) 或 方法形参注解(了解) |
| 位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 或 方法形参前面 |
| 作用 | 为 引用类型 属性设置值 |
| 属性 | required:true/false,定义该属性是否允许为null |
eg:
1 |
|
说明:
- @Autowired可以写在 属性 上,也可也写在 setter 方法上,最简单的处理方式是
写在属性上并将setter方法删除掉 - setter 方法 删除 的原因:
- 自动装配 基于反射设计 创建对象 并通过 暴力反射 为 私有属性进行设值
- 普通反射 只能获取 public 修饰的内容
- 暴力反射 除了获取 public 修饰的内容还可以获取 private 修改的内容
- 所以此处无需提供setter方法
- 自动装备建议使用无参构造方法创建对象(默认),如果不提供对应的构造方法,需要提供唯一的构造方法
如果 @Autowired 是按照类型注入的,给 BookDao 的两个实现起了名称,它还是有两个bean对象,为什么没有报错?
答:@Autowired默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配。因为变量名叫bookDao而容器中也有一个booDao,所以可以成功注入。
如果当根据类型在容器中找到多个bean,注入参数的属性名又和容器中 bean 的名称不一致,这个时候该如何解决,就需要使用到@Qualifier来指定注入哪个名称的bean对象
| 名称 | @Qualifier |
|---|---|
| 类型 | 属性注解 或 方法注解(了解) |
| 位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 |
| 作用 | 为引用类型属性指定注入的beanId |
| 属性 | value(默认):设置注入的beanId |
eg:
1 |
|
- @Qualifier 注解后的值就是需要注入的 bean 的名称
- @Qualifier 不能独立使用,必须和 @Autowired 一起使用
注解实现 简单类型 注入
使用 @Value 注解,将值写入注解的参数中就可以了
1 |
|
注解读取 properties配置文件
@Value 一般会被用在从 properties配置文件 中读取内容进行使用
| 名称 | @Value |
|---|---|
| 类型 | 属性注解 或 方法注解(了解) |
| 位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 |
| 作用 | 为 基本数据类型 或 字符串类型 属性设置值 |
| 属性 | value(默认):要注入的属性值 |
流程:
-
resource 下准备 properties 文件
1
name=heisenberg
-
使用 注解 加载 properties配置文件
- 在配置类上添加
@PropertySource注解
1
2
3
4
5
public class SpringConfig {
} - 在配置类上添加
-
使用 @Value 读取配置文件中的内容
1
2
3
4
5
6
7
8
public class BookDaoImpl implements BookDao {
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
注意:
-
如果读取的properties配置文件有多个,可以使用
@PropertySource的属性来指定多个1
-
@PropertySource注解属性中不支持使用通配符*,运行会报错1
-
@PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件1
| 名称 | @PropertySource |
|---|---|
| 类型 | 类注解 |
| 位置 | 类定义上方 |
| 作用 | 加载properties文件 中的属性值 |
| 属性 | value(默认):设置加载的properties文件对应的文件名或文件名组成的数组 |
注解开发管理 第三方bean
| 名称 | @Bean |
|---|---|
| 类型 | 方法注解 |
| 位置 | 方法定义上方 |
| 作用 | 设置该方法的返回值作为spring管理的bean |
| 属性 | value(默认):定义bean的id |
流程:
-
导入对应的 jar 包
1
2
3
4
5<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency> -
在 配置类中 添加一个方法,并在方法上添加
@Bean注解1
2
3
4
5
6
7
8
9
10
11
12
public class SpringConfig {
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}- 不能使用
DataSource ds = new DruidDataSource()
- 不能使用
-
从 IOC容器 中获取对象并打印
1
2
3
4
5
6
7public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource);
}
}- 如果有多个bean要被Spring管理,直接在配置类中多些几个方法,方法上添加 @Bean 注解即可
还有一种 扫描式 方案:可以不用加 @Configuration 注解,但是必须在Spring配置类上使用 @Import 注解手动引入需要加载的配置类
| 名称 | @Import |
|---|---|
| 类型 | 类注解 |
| 位置 | 类定义上方 |
| 作用 | 导入配置类 |
| 属性 | value(默认):定义导入的配置类类名, 当配置类有多个时使用数组格式一次性导入多个配置类 |
流程:
-
去除JdbcConfig类上的
@Configuration注解1
2
3
4
5
6
7
8
9
10
11public class JdbcConfig {
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
} -
在 Spring配置类 中引入
1
2
3
4
5
6
//@ComponentScan("com.itheima.config")
public class SpringConfig {
}
注意:
-
扫描注解可以移除
-
@Import 参数需要的是一个数组,可以引入多个配置类。
-
@Import 注解在配置类中 只能写一次
简单数据类型
使用@Value注解引入值
1 | public class JdbcConfig { |
引用数据类型
流程:
-
在 SpringConfig 中扫描 BookDao
- 扫描的目的是让 Spring 能管理到 BookDao,也就是说要让 IOC 容器中有一个 bookDao 对象
1
2
3
4
5
public class SpringConfig {
} -
在 JdbcConfig 类的方法上添加参数
1
2
3
4
5
6
7
8
9
10
public DataSource dataSource(BookDao bookDao){
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}- 引用类型注入 只需要为 bean 定义方法设置形参 即可,容器会根据类型自动装配对象
注解开发 总结

Spring 整合 MyBatis
Mybatis 流程

步骤1:准备数据库表
Mybatis是来操作数据库表,所以先创建一个数据库及表
1 | create database spring_db character set utf8; |
步骤2:创建项目导入jar包
项目的pom.xml添加相关依赖
1 | <dependencies> |
步骤3:根据表创建模型类
1 | public class Account implements Serializable { |
步骤4:创建Dao接口
1 | public interface AccountDao { |
步骤5:创建Service接口和实现类
1 | public interface AccountService { |
步骤6:添加jdbc.properties文件
resources目录下添加,用于配置数据库连接四要素
1 | jdbc.driver=com.mysql.jdbc.Driver |
useSSL:关闭MySQL的SSL连接
步骤7:添加Mybatis核心配置文件
1 |
|
步骤8:编写应用程序
1 | public class App { |
整合 Mybatis

说明:
- 第一行读取外部 properties 配置文件,Spring有提供具体的解决方案
@PropertySource,需要交给Spring - 第二行起别名包扫描,为 SqlSessionFactory 服务的,需要交给 Spring
- 第三行主要用于做连接池,Spring 之前我们已经整合了 Druid连接池,这块也需要交给 Spring
- 前面三行一起都是为了创建SqlSession对象用的,只需要将SqlSessionFactory交给Spring管理即可。
- 第四行是Mapper接口和映射文件[如果使用注解就没有该映射文件],这个是在获取到SqlSession以后执行具体操作的时候用,所以它和SqlSessionFactory创建的时机都不在同一个时间,可能需要单独管理。
流程:
-
创建项目导入 jar 包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<dependency>
<!--Spring操作数据库需要该jar包-->
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<!--
Spring与Mybatis整合的jar包
这个jar包mybatis在前面,是Mybatis提供的
-->
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency> -
创建 Spring 的主配置类
1
2
3
4
5
6//配置类注解
//包扫描,主要扫描的是项目中的AccountServiceImpl类
public class SpringConfig {
} -
创建数据源的配置类
- 在配置类中完成数据源的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class JdbcConfig {
private String driver;
private String url;
private String userName;
private String password;
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
} -
主配置类中读 properties 并引入数据源配置类
1
2
3
4
5
6
public class SpringConfig {
} -
创建 Mybatis配置类 并配置 SqlSessionFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class MybatisConfig {
//定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
//设置模型类的别名扫描
ssfb.setTypeAliasesPackage("com.itheima.domain");
//设置数据源
ssfb.setDataSource(dataSource);
return ssfb;
}
//定义bean,返回 MapperScannerConfigurer 对象
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}-
SqlSessionFactoryBean 是 FactoryBean 的一个子类,在该类中将 SqlSessionFactory 的创建进行了封装,简化对象的创建,只需要将其需要的内容设置即可。

- 方法中有一个参数为 dataSource,当前Spring容器中已经创建了Druid数据源,类型刚好是DataSource类型,此时在初始化SqlSessionFactoryBean这个对象的时候,发现需要使用DataSource对象,而容器中刚好有这么一个对象,就自动加载了 DruidDataSource 对象
-
MapperScannerConfigurer 对象也是MyBatis提供的专用于整合的jar包中的类,用来处理原始配置文件中的 mappers 相关配置,加载数据层的 Mapper 接口类

- MapperScannerConfigurer有一个核心属性 basePackage,就是用来设置所扫描的包路径
-
-
主配置类中引入 Mybatis配置类
1
2
3
4
5
6
public class SpringConfig {
} -
编写运行类
1
2
3
4
5
6
7
8
9
10public class App {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
AccountService accountService = ctx.getBean(AccountService.class);
Account ac = accountService.findById(1);
System.out.println(ac);
}
}
支持Spring与Mybatis的整合就已经完成了,其中主要用到的两个类分别是:
SqlSessionFactoryBeanMapperScannerConfigurer
-
Spring整合Junit
名称 @RunWith 类型 测试类注解 位置 测试类定义上方 作用 设置JUnit运行器 属性 value(默认):运行所使用的运行期 名称 @ContextConfiguration 类型 测试类注解 位置 测试类定义上方 作用 设置JUnit加载的Spring核心配置 属性 classes:核心配置类,可以使用数组的格式设定加载多个配置类 locations:配置文件,可以使用数组的格式设定加载多个配置文件名称 -
引入依赖
1
2
3
4
5
6
7
8
9
10
11
12<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency> -
在 test\java 下创建一个 AccountServiceTest,这个名字任意
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//设置类运行器
//设置Spring环境对应的配置类
//加载配置类
//@ContextConfiguration(locations={"classpath:applicationContext.xml"})//加载配置文件
public class AccountServiceTest {
//支持自动装配注入bean
private AccountService accountService;
public void testFindById(){
System.out.println(accountService.findById(1));
}
public void testFindAll(){
System.out.println(accountService.findAll());
}
}- 单元测试,如果测试的是 注解配置类,则使用
@ContextConfiguration(classes = 配置类.class) - 单元测试,如果测试的是 配置文件,则使用
@ContextConfiguration(locations={配置文件名,...}) - Junit 运行后是基于 Spring 环境运行的,所以 Spring 提供了一个专用的类运行器,这个务必要设置,这个类运行器就在 Spring 的测试专用包中提供的,导入的坐标就是这个东西
SpringJUnit4ClassRunner - 上面两个配置都是固定格式,当需要测试哪个bean时,使用自动装配加载对应的对象,下面的工作就和以前做Junit单元测试完全一样了
- 单元测试,如果测试的是 注解配置类,则使用
-
AOP
AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构
- OOP(Object Oriented Programming)面向对象编程
AOP的作用: 在不改原有代码的前提下对其进行功能增强
在不惊动(改动)原有设计(代码)的前提下,想给谁添加功能就给谁添加
- 这个就是Spring的理念:无入侵式/无侵入式
AOP 核心概念

Spring 的 AOP 是对一个类的方法在 不进行任何修改的前提 下实现增强
对于上面的案例中:
- BookServiceImpl 中有
save、update、delete和select方法,这些方法我们给起了一个名字叫 连接点 - 在BookServiceImpl的四个方法中,
update和delete只有打印 没有计算万次执行消耗时间,但是在运行的时候已经有该功能,那也就是说update和delete方法都已经被增强,所以对于需要增强的方法叫 切入点 - 执行BookServiceImpl的
update和delete方法的时候都被添加了一个计算万次执行消耗时间的功能,将这个功能抽取到一个方法中,换句话说就是 存放共性功能 的方法叫做 通知 - 通知是要增强的内容,会有多个,切入点是需要被增强的方法,也会有多个,那 哪个切入点需要添加哪个通知,就需要提前将它们之间的关系描述清楚,那么对于 通知和切入点之间的关系 描述叫做 切面
- 通知 是一个 方法,方法 不能独立存在需要被写在 一个类 中,这个类叫做 通知类
总结:
-
代理 (proxy):SpringAOP的核心本质是采用 代理模式 实现的
-
连接点 (JoinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
- 在SpringAOP中,理解为 任意方法的执行
-
切入点 (Pointcut):匹配连接点的式子
- 在SpringAOP中,一个切入点可以描述一个具体方法,也可也匹配多个方法
- 可以理解为:具有共性功能的方法描述
- 一个具体的方法:如 com.itheima.dao 包下的 BookDao 接口中的无形参无返回值的 save 方法
- 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
- 连接点范围要比切入点范围大,是切入点的方法也一定是连接点,但是是连接点的方法就不一定要被增强,所以可能不是切入点。
-
通知 (Advice):在切入点处执行的操作,也就是共性功能
- 可以理解为:若干个方法的共性功能,在切入点处执行,最终体现为一个方法
- 在SpringAOP中,功能最终 以方法的形式 呈现
-
通知类:定义通知的类
-
切面 (Aspect):描述 通知与切入点的对应关系
-
目标对象 (target):被代理的原始对象称为目标对象
AOP 入门案例分析
采用 注解 完成 AOP的开发
思路分析:
- 导入坐标 (pom.xml)
- 制作连接点 (原始操作,Dao接口与实现类)
- 制作共性功能 (通知类与通知)
- 定义切入点
- 绑定切入点与通知关系 (切面)
流程:
-
添加依赖
1
2
3
4
5<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>- 因为
spring-context中已经导入了spring-aop,所以不需要再单独导入spring-aop - 导入 AspectJ 的 jar 包,AspectJ是AOP思想的一个具体实现,Spring有自己的AOP实现,但是相比于AspectJ来说比较麻烦,所以我们直接采用Spring整合ApsectJ的方式进行AOP开发
- 因为
-
添加 BookDao 和 BookDaoImpl 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public interface BookDao {
public void save();
public void update();
}
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println(System.currentTimeMillis());
System.out.println("book dao save ...");
}
public void update(){
System.out.println("book dao update ...");
}
} -
定义 通知类 和 通知
- 通知 就是 将共性功能抽取出来后形成的方法,共性功能 指的就是 当前系统时间的打印
1
2
3
4
5public class MyAdvice {
public void method(){
System.out.println(System.currentTimeMillis());
}
} -
定义切入点
名称 @Pointcut 类型 方法注解 位置 切入点方法定义上方 作用 设置切入点方法 属性 value(默认):切入点表达式 1
2
3
4
5
6
7
8
9
10public class MyAdvice {
// 这块起始就是定义了一个位置
private void pt(){}
public void method(){
System.out.println(System.currentTimeMillis());
}
}- 切入点 定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑
-
制作切面
- 绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行 位置
名称 @Before 类型 方法注解 位置 通知方法定义上方 作用 设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行 1
2
3
4
5
6
7
8
9public class MyAdvice {
private void pt(){}
public void method(){
System.out.println(System.currentTimeMillis());
}
}- @Before 翻译过来是 之前,也就是说 通知会在切入点方法执行 之前执行

-
将通知类 配给 容器 并标识其为 切面类
名称 @Aspect 类型 类注解 位置 切面类定义上方 作用 设置当前类为AOP切面类 1
2
3
4
5
6
7
8
9
10
11
public class MyAdvice {
private void pt(){}
public void method(){
System.out.println(System.currentTimeMillis());
}
} -
开启注解格式 AOP 功能
名称 @EnableAspectJAutoProxy 类型 配置类注解 位置 配置类定义上方 作用 开启注解格式AOP功能 1
2
3
4
5
public class SpringConfig {
} -
运行程序
1
2
3
4
5
6
7public class App {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
bookDao.update();
}
}
AOP 工作流程
AOP 是 基于Spring容器管理的bean 做的增强
SpringAOP 本质:代理模式
-
Spring容器启动
- 容器启动就需要去加载bean,哪些类需要被加载呢?
- 需要被增强的类,如:BookServiceImpl
- 通知类,如:MyAdvice
- 注意此时bean对象还没有创建成功
-
读取所有切面配置中的 切入点

- 如果有两个切入点的配置,但是第一个
ptx()并没有被使用,则不会被读取
- 如果有两个切入点的配置,但是第一个
-
初始化bean,判定bean对应的类中的方法是否匹配到任意切入点

- 匹配失败,创建原始对象,如
UserDao - 匹配成功,创建原始对象(目标对象)的 代理对象,如:
BookDao
- 匹配失败,创建原始对象,如
-
获取bean执行方法
- 获取的bean是 原始对象 时,调用方法并执行,完成操作
- 获取的bean是 代理对象 时,根据 代理对象的运行模式 运行 原始方法与增强的内容,完成操作
目标对象 (Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
代理 (Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现
AOP 切入点表达式
切入点:要 进行增强 的 方法
切入点表达式:要 进行增强的方法 的 描述方式
eg:

描述方式一:执行 com.itheima.dao 包下的 BookDao 接口中的 无参数update 方法
1 | execution(void com.itheima.dao.BookDao.update()) |
描述方式二:执行 com.itheima.dao.impl 包下的 BookDaoImpl 类中的 无参数update 方法
1 | execution(void com.itheima.dao.impl.BookDaoImpl.update()) |
因为调用接口方法的时候 最终运行的还是其实现类 的方法,所以上面两种描述方式都是可以的。
切入点表达式 的 标准格式:
1 | 动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名) |
eg:
1 | execution(public User com.itheima.service.UserService.findById(int)) |
- execution:动作关键字,描述切入点的行为动作,例如 execution 表示 执行到指定切入点
- public:访问修饰符,还可以是 public,private 等,可以省略
- User:返回值,写返回值类型
- com.itheima.service:包名,多级包使用点连接
- UserService:类/接口名称
- findById:方法名
- int:参数,直接写参数的类型,多个类型用逗号隔开
- 异常名:方法定义中抛出指定异常,可以省略
AOP 切入点表达式 的 通配符
使用通配符描述切入点,主要的目的就是简化之前的配置,具体的通配符有:
-
*:单个独立 的 任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现1
execution(public * com.itheima.*.UserService.find*(*))
- 匹配 com.itheima 包下的 任意包 中的 UserService类或接口 中所有 find 开头的 带有一个参数 的方法
-
..:多个连续 的 任意符号,可以独立出现,常用于简化包名与参数的书写1
execution(public User com..UserService.findById(..))
- 匹配 com 包下的 任意包 中的 UserService类或接口 中 所有名称 为 findById 的方法
-
+:专用于匹配子类类型1
execution(* *..*Service+.*(..))
- 这个使用率较低,描述子类的,继承机会就一次,使用都很慎重,很少使用
- *Service+,表示所有以 Service 结尾的接口的子类
书写技巧:
- 所有代码按照标准规范开发,否则以下技巧全部失效
- 描述切入点 通常描述接口,而不描述实现类,如果描述到实现类,就出现紧耦合了
- 访问控制修饰符针对接口开发均采用 public 描述( 可省略访问控制修饰符描述)
- 返回值类型对于 增删改 类使用 精准类型加速匹配,对于查询类使用
*通配快速描述 - 包名 书写 尽量不使用 … 匹配,效率过低,常用
*做单个包描述匹配,或精准匹配 - 接口名/类名 书写名称与模块相关的 采用 * 匹配,例如 UserService 书写成 *Service,绑定业务层接口名
- 方法名 书写以 动词 进行 精准匹配,名词采用
*匹配,例如 getById 书写成 getBy*,selectAll书写成selectAll - 参数规则较为复杂,根据业务方法灵活调整
- 通常 不使用异常 作为 匹配 规则
AOP 通知类型
AOP 通知分为 5 种类型:
-
前置通知
@Before()1
2
3
4
5
6
7
8
9
10
11
12
public class MyAdvice {
private void pt(){}
//此处也可以写成 @Before("MyAdvice.pt()"),不建议
public void before() {
System.out.println("before advice ...");
}
} -
后置通知
@After()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyAdvice {
private void pt(){}
public void before() {
System.out.println("before advice ...");
}
public void after() {
System.out.println("after advice ...");
}
} -
环绕通知
@Around()- 环绕通知功能比较强大,它可以 追加功能到 方法执行的前后
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyAdvice {
private void pt(){}
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around before advice ...");
//表示对原始操作的调用
pjp.proceed();
System.out.println("around after advice ...");
}
} -
返回后通知 (了解)
AfterReturning() -
抛出异常后通知 (了解)
@AfterThrowing()

@Around() 注意事项:
- 环绕通知必须依赖形参 ProceedingJoinPoint 才能实现 对原始方法的调用,进而实现原始方法调用前后同时添加通知
- 通知中如果 未使用ProceedingJoinPoint 对原始方法进行调用 将跳过原始方法 的执行
- 对原始方法的调用可以不接收返回值,通知方法设置成 void 即可,如果接收返回值,最好设定为 Object类型
- 原始方法的返回值如果是 void 类型,通知方法的返回值类型可以设置成 void,也可以设置成 Object
- 由于 无法预知原始方法运行后 是否会抛出异常,因此环绕通知方法必须要处理 Throwable 异常
1 |
|
- 返回的是 Object 而不是 int 的主要原因:Object 类型 更通用
AOP 通知获取数据
获取切入点方法的参数,所有的通知类型都可以获取参数
-
JoinPoint:适用于前置、后置、返回后、抛出异常后通知
1
2
3
4
5
6
public void before(JoinPoint jp)
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("before advice ..." );
} -
ProceedingJoinPoint:适用于 环绕通知
1
2
3
4
5
6
7
public Object around(ProceedingJoinPoint pjp)throws Throwable {
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
Object ret = pjp.proceed();
return ret;
}
获取切入点方法返回值,前置 和 抛出异常后通知 是没有返回值,后置通知可有可无,所以不做研究
-
返回后通知,可以获得切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
1
2
3
4
public void afterReturning(Object ret) {
System.out.println("afterReturning advice ..."+ret);
} -
环绕通知,可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值
1
2
3
4
5
6
7
8
9
10
public Object around(ProceedingJoinPoint pjp) throws Throwable{
// 获取参数
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0] = 666;
Object ret = pjp.proceed(args);
return ret;
}
获取切入点方法运行异常信息,前置 和 返回后通知 是不会有,后置通知 可有可无,所以不做研究
-
抛出异常后通知
1
2
3
4
public void afterThrowing(Throwable t) {
System.out.println("afterThrowing advice ..."+t);
} -
环绕通知
1
2
3
4
5
6
7
8
9
10
11
12
public Object around(ProceedingJoinPoint pjp){
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
Object ret = null;
try{
ret = pjp.proceed(args);
}catch(Throwable throwable){
t.printStackTrace();
}
return ret;
}
Spring 事务
事务 作用:在 数据层 保障一系列的数据库操作同成功同失败
Spring事务 作用:在 数据层 或 业务层 保障一系列的数据库操作同成功同失败
Spring为了管理事务,提供了一个平台事务管理器 PlatformTransactionManager
1 | public interface PlatformTransactionManager extends TransactionManager { |
PlatformTransactionManager 只是一个 接口,Spring 还为其提供了一个 具体的实现
1 | public class DataSourceTransactionManager { |
从名称上可以看出,只需要给它一个 DataSource 对象,它就可以在 业务层 管理事务。其内部采用的是JDBC的事务。所以说如果 持久层 采用的是 JDBC 相关的技术,就可以采用这个事务管理器来管理事务。而Mybatis内部采用的就是 JDBC 的事务,所以Spring整合 Mybatis 就采用的这个 DataSourceTransactionManager 事务管理器
Spring事务 管理流程
-
在需要 被事务管理的方法 上 添加注解
名称 @Transactional 类型 接口注解 类注解 方法注解 位置 业务层接口上方 业务层实现类上方 业务方法上方 作用 为当前业务层方法添加事务(如果设置在类或接口上方则类或接口中所有方法均添加事务) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public interface AccountService {
/**
* 转账操作
* @param out 传出方
* @param in 转入方
* @param money 金额
*/
//配置当前接口方法具有事务
public void transfer(String out,String in ,Double money) ;
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void transfer(String out,String in ,Double money) {
accountDao.outMoney(out,money);
int i = 1/0;
accountDao.inMoney(in,money);
}
}@Transactional可以写在 接口类上、接口方法上、实现类上 和 实现类方法上- 写在接口类上,该接口的所有实现类的所有方法都会有事务
- 写在接口方法上,该接口的所有实现类的该方法都会有事务
- 写在实现类上,该类中的所有方法都会有事务
- 写在实现类方法上,该方法上有事务
- 建议写在 实现类 或 实现类的方法上
-
在 JdbcConfig 类中配置事务管理器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class JdbcConfig {
private String driver;
private String url;
private String userName;
private String password;
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
//配置事务管理器,mybatis使用的是jdbc事务
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
} -
在 SpringConfig 的配置类中开启
名称 @EnableTransactionManagement 类型 配置类注解 位置 配置类定义上方 作用 设置当前Spring环境中开启注解式事务支持 1
2
3
4
5
6
7
8- 事务管理器要根据使用技术进行选择,Mybatis 框架使用的是 JDBC 事务,可以直接使用
DataSourceTransactionManager
- 事务管理器要根据使用技术进行选择,Mybatis 框架使用的是 JDBC 事务,可以直接使用
Spring事务 角色

- 事务管理员:发起事务方,在Spring中通常指代业务层 开启事务的方法
- 事务协调员:加入事务方,在Spring中通常指代 数据层方法,也可以是 业务层方法
Spring事务 属性

注意:
Spring的事务只会对 Error异常 和 RuntimeException异常 及其子类进行事务回滚,其他的异常类型是不会回滚的
事务的传播行为
事务的传播行为:事务协调员对事务管理员所携带事务的处理态度,涉及到 propagation属性
流程:
-
修改 logService 改变事务的传播行为
1
2
3
4
5
6
7
8
9
10
11
12
public class LogServiceImpl implements LogService {
private LogDao logDao;
//propagation设置事务属性:传播行为设置为REQUIRES_NEW(当前操作需要新事务)
public void log(String out,String in,Double money ) {
logDao.log("转账操作由"+ out +"到"+ in +",金额:"+money);
}
} -
事务传播行为 的 可选值

SpringMVC

SpringMVC 概述
SpringMVC是 一种 基于Java实现 MVC模型 的 轻量级Web框架
优点:
- 使用简单、开发便捷(相比于 Servlet)
- 灵活性强
SpringMVC 入门案例
流程:
-
导入 jar 包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<dependencies>
<dependency>
<!-- servlet的坐标 -->
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<!-- springmvc的坐标 -->
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies> -
创建SpringMVC控制器类(等同于Servlet功能)
名称 @Controller 类型 类注解 位置 SpringMVC控制器类定义上方 作用 设定SpringMVC的核心控制器bean 名称 @RequestMapping 类型 类注解 或 方法注解 位置 SpringMVC控制器类或方法定义上方 作用 设置当前控制器方法 请求访问路径,配置映射路径 相关属性 value(默认),请求访问路径 名称 @ResponseBody 类型 类注解 或 方法注解 位置 SpringMVC控制器类或方法定义上方 作用 设置当前控制器方法响应内容为 当前返回值,无需解析,返回json数据 1
2
3
4
5
6
7
8
9
10
11
12
13
14// 使用@controller定义bean
public class UserController {
// 设置当前操作的访问路径
// 设置当前操作的返回值类型
public String save(){
System.out.println("user save ...");
return "{'info':'springmvc'}";
}
} -
初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
1
2
3
4
public class SpringMvcConfig {
} -
初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 定义一个servlet容器启动的配置类,在里面加载spring的配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
// 加载springmvc容器配置
protected WebApplicationContext createServletApplicationContext() {
// 初始化WebApplicationContext对象
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// 加载指定配置类
ctx.register(SpringMvcConfig.class);
return ctx;
}
// 设置哪些请求归属springmvc处理
protected String[] getServletMappings() {
return new String[]{"/"};
}
// 加载spring容器配置
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
SpringMVC入门案例总结:(1 + N)
-
一次性工作
- 创建工程,设置服务器,加载工程
- 导入坐标
- 创建 web容器 启动类,加载 SpringMVC配置,并设置 SpringMVC请求拦截路径
- SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
-
多次工作
- 定义处理请求的控制器类
- 定义处理请求的控制器方法,并配置映射路径(@RequestMapping)与返回json数据(@ResponseBody)
-
AbstractDispatcherServletInitializer类 是 SpringMVC 提供的 快速初始化Web3.0容器 的 抽象类
-
AbstractDispatcherServletInitializer 提供了 三个接口方法 供用户实现:
-
createServletApplicationContext() 方法:创建Servlet容器时,加载SpringMVC对应的bean并放入 WebApplicationContext 对象范围中,而 WebApplicationContext 的作用范围为 ServletContext 范围,即整个web容器范围
-
createServletApplicationContext:用来加载 SpringMVC 环境
-
getServletMappings() 方法:设定 SpringMVC 对应的 请求映射路径,即 SpringMVC 拦截哪些请求
1
2
3
4// 设置为 / 表示拦截所有请求,任意请求都将转入SpringMVC处理
protected String[] getServletMappings() {
return new String[]{"/"};
} -
createRootApplicationContext() 方法:如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式和 createServletApplicationContext 相同
-
createRootApplicationContext:用来加载 Spring 环境
-

SpringMVC 工作流程
启动服务器初始化过程
流程:
-
服务器启动,执行 ServletContainersInitConfig 类,初始化web容器
- 功能类似于以前的 web.xml
-
执行 createServletApplicationContext 方法,创建了 WebApplicationContext 对象
- 该方法加载 SpringMVC 的配置类 SpringMvcConfig 来初始化 SpringMVC 的容器
-
加载 SpringMvcConfig 配置类

-
执行 @ComponentScan 加载对应的 bean
- 扫描指定包及其子包下所有类上的注解,如 Controller 类上的 @Controller 注解
-
加载 UserController,每个 @RequestMapping 的名称对应一个具体的方法

- 此时就建立了
/save和 save 方法的对应关系
- 此时就建立了
-
执行 getServletMappings 方法,设定 SpringMVC 拦截请求的路径规则

/代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求
单次请求过程
流程:
- 发送请求
http://localhost/save - web容器发现该请求满足 SpringMVC 拦截规则,将请求交给 SpringMVC 处理
- 解析请求路径 /save
- 由 /save 匹配执行对应的方法 save()
- 上面的第五步已经将请求路径和方法建立了对应关系,通过 /save 就能找到对应的 save 方法
- 执行 save()
- 检测到有 @ResponseBody 直接将 save() 方法的返回值作为响应体返回给请求方
bean 加载控制
现在存在的问题:
- SpringMVC 加载其相关 bean (表现层bean),也就是 controller 包下的类
- Spring 控制的 bean
- 业务 bean (Service)
- 功能 bean (DataSource、SqlSessionFactoryBean、MapperScannerConfigurer等)
- 如何让 Spring 和 SpringMVC 分开加载各自的内容?
- 答:加载 Spring 控制的 bean 的时候排除掉 SpringMVC 控制的 bean
方案:
| 名称 | @ComponentScan |
|---|---|
| 类型 | 类注解 |
| 位置 | 类定义上方 |
| 作用 | 设置spring配置类扫描路径,用于加载使用注解格式定义的bean |
| 相关属性 | excludeFilters:排除扫描路径中加载的bean,需要指定类别 (type) 和具体项 (classes) includeFilters:加载指定的bean,需要指定类别 (type) 和具体项 (classes) |
-
方式一:Spring 加载的 bean 设定扫描范围为 精准范围,例如service包、dao包等
1
2
3
4
public class SpringConfig {
}- Dao最终是交给
MapperScannerConfigurer对象来进行扫描处理的,只需要将其扫描到service包即可
- Dao最终是交给
-
方式二:Spring 加载的 bean 设定扫描范围为 com.itheima,排除掉 controller 包中的 bean
1
2
3
4
5
6
7
8
9
public class SpringConfig {
}-
excludeFilters 属性:设置扫描加载 bean 时,排除的 过滤规则
-
type属性:设置 排除规则,当前使用按照 bean 定义时的注解类型进行排除
- ANNOTATION:按照注解排除(常用)
- ASSIGNABLE_TYPE:按照指定的类型过滤
- ASPECTJ:按照 Aspectj 表达式排除,基本上不会用
- REGEX:按照正则表达式排除
- CUSTOM:按照自定义规则排除
-
classes属性:设置 排除的具体注解类,当前设置排除 @Controller 定义的 bean
-
-
方式三:不区分 Spring 与 SpringMVC 的环境,加载到同一个环境中(了解)
有了Spring的配置类,要想在 tomcat服务器 启动将其加载,需要修改ServletContainersInitConfig
1 | public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { |
对于上述的配置方式,Spring还提供了一种更简单的配置方式,可以不用再去创建AnnotationConfigWebApplicationContext 对象,不用手动register对应的配置类
1 | public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { |
请求 与 响应
请求 的 映射路径
设置映射路径的流程:
-
修改 Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class UserController {
public String save(){
System.out.println("user save ...");
return "{'module':'user save'}";
}
public String save(){
System.out.println("user delete ...");
return "{'module':'user delete'}";
}
}
public class BookController {
public String save(){
System.out.println("book save ...");
return "{'module':'book save'}";
}
} -
优化路径配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class UserController {
public String save(){
System.out.println("user save ...");
return "{'module':'user save'}";
}
public String save(){
System.out.println("user delete ...");
return "{'module':'user delete'}";
}
}
public class BookController {
public String save(){
System.out.println("book save ...");
return "{'module':'book save'}";
}
}
注意:
- 当类上和方法上都添加了
@RequestMapping注解,前端发送请求的时候,要和两个注解的 value 值相加匹配才能访问到 - @RequestMapping 注解 value 属性前面加不加
/都可以
请求参数
Get 请求
Post 请求
解决Post请求中文乱码问题:
1 | public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { |
如果前端给的是name,后台接收使用的是userName,两个名称对不上,导致接收数据失败,使用@RequestParam 注解
| 名称 | @RequestParam |
|---|---|
| 类型 | 形参注解 |
| 位置 | SpringMVC控制器方法形参定义前面 |
| 作用 | 绑定请求参数与处理器方法形参间的关系 |
| 相关参数 | required:是否为必传参数 defaultValue:参数默认值 |
1 |
|
JSON数据 传输参数
对于JSON数据类型,我们常见的有三种:
- json普通数组
- ([“value1”,“value2”,“value3”,…])
- json对象
- ({key1:value1,key2:value2,…})
- json对象数组
- ([{key1:value1,…},{key2:value2,…}])
流程:
-
pom.xml 添加依赖
1
2
3
4
5<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency> -
开启 SpringMVC 注解支持
名称 @EnableWebMvc 类型 配置类注解 位置 SpringMVC配置类定义上方 作用 开启SpringMVC多项辅助功能 1
2
3
4
5
6
//开启json数据类型自动转换
public class SpringMvcConfig {
} -
参数前添加
@RequestBody名称 @RequestBody 类型 形参注解 位置 SpringMVC控制器方法形参定义前面 作用 将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次 1
2
3
4
5
6
7// 使用@RequestBody注解 将外部传递的json数组数据 映射到 形参的集合对象中 作为数据
public String listParamForJson( List<String> likes){
System.out.println("list common(json)参数传递 list ==> "+likes);
return "{'module':'list common for json param'}";
}
@RequestBody 与 @RequestParam 区别:
-
区别
- @RequestParam 用于 接收 url地址 传参,表单传参【application/x-www-form-urlencoded】
- @RequestBody 用于 接收 json 数据【application/json】
-
应用
- 后期开发中,发送 json 格式数据为主,@RequestBody 应用较广
- 如果发送 非json 格式数据,选用 @RequestParam 接收请求参数
日期类型 参数传递
SpringMVC 默认支持的 字符串转日期 的格式为 yyyy/MM/dd,但如果发送 yyyy-MM-dd,不符合其默认格式,SpringMVC 就无法进行格式转换,解决办法是使用 @DateTimeFormat注解
| 名称 | @DateTimeFormat |
|---|---|
| 类型 | ==形参注解== |
| 位置 | SpringMVC控制器方法形参前面 |
| 作用 | 设定日期时间型数据格式 |
| 相关属性 | pattern:指定日期时间格式字符串 |
eg:
1 |
|
内部实现原理:
SpringMVC 中提供了 很多类型转换接口和实现类,例如:Converter接口
- Converter 所属的包为
org.springframework.core.convert.converter
1 | /** |
HttpMessageConverter 接口
- 该接口是实现 对象与JSON 之间的转换工作
SpringMVC的配置类把 @EnableWebMvc 当做标配配置上去
响应 JSON数据
| 名称 | @ResponseBody |
|---|---|
| 类型 | 方法 / 类注解 |
| 位置 | SpringMVC控制器方法定义上方和控制类上 |
| 作用 | 设置当前控制器返回值作为响应体,写在类上,该类的所有方法都有该注解功能 |
| 相关属性 | pattern:指定日期时间格式字符串 |
响应POJO对象:
1 |
|
- 返回值为 实体类 对象,设置返回值为 实体类 类型,即可实现返回对应对象的 json 数据,需要依赖
@ResponseBody注解和@EnableWebMvc注解
响应POJO集合对象:
1 |
|
@ResponseBody注解说明:
- 该注解可以写在 类上 或者 方法上
- 写在类上就是该类下的所有方法都有
@ReponseBody功能 - 当方法上有
@ReponseBody注解后- 方法的返回值为 字符串,会将其作为 文本内容 直接响应给 前端
- 方法的返回值为 对象,会将 对象 转换成 JSON 响应给 前端
此处又使用到了类型转换,内部还是通过 Converter接口 (HttpMessageConverter)的实现类完成的,所以Converter除了前面所说的功能外,它还可以实现:
- 对象 转 Json数据 (POJO -> json)
- 集合 转 Json数据 (Collection -> json)
Rest 风格
REST(Representational State Transfer),表现形式状态转换,它是一种软件架构 风格
当想表示一个网络资源的时候,可以使用两种方式:
- 传统风格 资源描述形式
http://localhost/user/getById?id=1查询id为1的用户信息http://localhost/user/saveUser保存用户信息
- REST风格 描述形式
http://localhost/user/1http://localhost/user
REST的优点有:
- 隐藏资源 的 访问行为,无法通过 地址 得知对资源是何种操作
- 书写简化
按照 REST风格 访问资源时使用 行为动作 区分对资源进行了何种操作
http://localhost/users查询全部用户信息 GET(查询)http://localhost/users/1查询指定用户信息 GET(查询)http://localhost/users添加用户信息 POST(新增 / 保存)http://localhost/users修改用户信息 PUT(修改 / 更新)http://localhost/users/1删除用户信息 DELETE(删除)
请求的方式比较多,但是比较常用的就4种,分别是GET、POST、PUT、DELETE。
按照 不同的请求方式 代表不同的操作类型:
- 发送 GET 请求是用来做 查询
- 发送 POST 请求是用来做 新增
- 发送 PUT 请求是用来做 修改
- 发送 DELETE 请求是用来做 删除
根据 REST风格 对资源进行访问称为 RESTful
注意:
上述行为是约定方式,约定不是规范,可以打破,所以称 REST风格,而不是REST规范
- REST提供了对应的架构方式,按照这种架构设计项目 可以降低开发的复杂性,提高系统的可伸缩性
- REST中规定 GET / POST / PUT / DELETE 针对的是 查询 / 新增 / 修改 / 删除,但是我们如果非要用 GET 请求做删除,这点在程序上运行是可以实现的
- 描述模块的名称通常使用复数,也就是加 s 的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts…
| 名称 | @PathVariable |
|---|---|
| 类型 | 形参注解 |
| 位置 | SpringMVC控制器方法形参定义前面 |
| 作用 | 绑定路径参数 与 处理器方法形参 间的关系,要求路径参数名与形参名一一对应 |
eg:
1 |
|
@RequestBody、@RequestParam、@PathVariable的区别:
- 区别
- @RequestParam 用于 接收 url地址 传参 或 表单传参
- @RequestBody 用于 接收 json 数据
- @PathVariable 用于 接收 路径参数,使用
{参数名称}描述 路径参数
- 应用
- 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
- 如果发送 非json格式 数据,选用 @RequestParam 接收请求参数
- 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用 @PathVariable 接收请求路径变量,通常用于 传递id值
Restful 风格案例
| 名称 | @RestController |
|---|---|
| 类型 | 类注解 |
| 位置 | 基于SpringMVC的RESTful开发控制器类定义上方 |
| 作用 | 设置当前控制器类为 RESTful风格, 等同于 @Controller 与 @ResponseBody 两个注解组合功能 |
| 名称 | @GetMapping @PostMapping @PutMapping @DeleteMapping |
|---|---|
| 类型 | 方法注解 |
| 位置 | 基于 SpringMVC 的 RESTful 开发控制器方法定义上方 |
| 作用 | 设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作, 例如: @GetMapping 对应GET请求 |
| 相关属性 | value(默认):请求访问路径 |
eg:
1 | //@Controller + ReponseBody |
SpringMVC 页面访问处理
流程:
-
将静态页面放到 项目的
webapp目录下
-
SpringMVC 会 拦截静态资源,SpringMVC需要将 静态资源 进行 放行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SpringMvcSupport extends WebMvcConfigurationSupport {
// 设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 当访问请求为 /pages/???? 时候不要走Mvc,从 /pages 目录下 查找内容
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}- 该配置类是在 config 目录下,SpringMVC 扫描的是 controller 包,所以该配置类还未生效,要想生效需要将 SpringMvcConfig 配置类进行修改
-
SpringMvcConfig 配置类进行修改
1
2
3
4
5
6
7
8
9
10
11
12
13
public class SpringMvcConfig {
}
// 或者
public class SpringMvcConfig {
}- Spring的容器不能访问SpringMVC的容器,SpringMVC的容器可以访问Spring的容器
SSM 整合
流程分析
- 创建Maven工程
- 编写Web项目的入口配置类,实现
AbstractAnnotationConfigDispatcherServletInitializer重写以下方法- getRootConfigClasses():返回Spring的配置类
- 需要 SpringConfig 配置类
- getServletConfigClasses() :返回SpringMVC的配置类
- 需要 SpringMvcConfig 配置类
- getServletMappings():设置SpringMVC请求拦截路径规则
- getServletFilters() :设置过滤器,解决POST请求中文乱码问题
- getRootConfigClasses():返回Spring的配置类
- 编写Web项目的入口配置类,实现
- SSM整合
- SpringConfig
- 标识该类为 配置类
@Configuration - 扫描 Service 所在的包
@ComponentScan - 在Service 层要 管理事务
@EnableTransactionManagement - 读取外部的 properties 配置文件
@PropertySource - 整合Mybatis需要引入Mybatis相关配置类
@Import- 第三方数据源配置类 JdbcConfig
- 构建DataSource数据源,DruidDataSouroce,需要注入数据库连接四要素,@Bean @Value
- 构建平台事务管理器,DataSourceTransactionManager,@Bean
- Mybatis配置类 MybatisConfig
- 构建SqlSessionFactoryBean并设置别名扫描与数据源,@Bean
- 构建MapperScannerConfigurer并设置DAO层的包扫描
- 第三方数据源配置类 JdbcConfig
- 标识该类为 配置类
- SpringMvcConfig
- 标识该类为配置类
@Configuration - 扫描Controller所在的包
@ComponentScan - 开启SpringMVC注解支持
@EnableWebMvc
- 标识该类为配置类
- SpringConfig
- 功能模块
- 创建 数据库表
- 根据 数据库表 创建对应的 模型类
- 通过 Dao 层完成数据库表的增删改查(接口 + 自动代理)
- 编写Service层(Service接口 + 实现类)
@Service@Transactional- 整合Junit对业务层进行单元测试
@RunWith@ContextConfiguration@Test
- 编写Controller层
- 接收请求
@RequestMapping、@GetMapping、@PostMapping、@PutMapping、@DeleteMapping - 接收数据 简单、POJO、嵌套POJO、集合、数组、JSON数据类型
@RequestParam@PathVariable@RequestBody
- 转发业务层
@Autowired
- 响应结果
@ResponseBody
- 接收请求
整合配置
流程:
-
添加依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies> -
创建项目包结构
- config 目录存放的是 相关的配置类
- controller 编写的是 Controller 类
- dao 存放的是 Dao 接口,因为使用的是 Mapper接口代理 方式,所以没有实现类包
- service 存的是 Service 接口,impl 存放的是 Service实现类
- resources 存入的是配置文件,如:Jdbc.properties
- webapp 目录可以存放 静态资源
- test/java 存放的是 测试类
-
创建 SpringConfig 配置类
1
2
3
4
5
6
7
public class SpringConfig {
} -
创建 JdbcConfig 配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27public class JdbcConfig {
private String driver;
private String url;
private String username;
private String password;
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
} -
创建 MybatisConfig 配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class MyBatisConfig {
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.itheima.domain");
return factoryBean;
}
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
} -
创建 jdbc.properties
1
2
3
4jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db
jdbc.username=root
jdbc.password=root -
创建 SpringMVC 配置类
1
2
3
4
5
public class SpringMvcConfig {
} -
创建 Web项目 入口配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
//加载Spring配置类
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
//加载SpringMVC配置类
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
//设置SpringMVC请求地址拦截规则
protected String[] getServletMappings() {
return new String[]{"/"};
}
//设置post请求中文乱码过滤器
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("utf-8");
return new Filter[]{filter};
}
}
统一结果封装
表现层数据封装
为了封装返回的结果数据:创建结果模型类,封装数据到 data 属性中
为了封装返回的数据是何种操作及是否操作成功:封装操作结果到code属性中
操作失败后为了封装返回的错误信息:封装特殊消息到 message(msg) 属性中
所以,设置统一数据返回结果类:
1 | public class Result{ |
注意:Result类名及类中的字段并不是固定的,可以根据需要自行增减提供若干个构造方法,方便操作
结果封装
对于结果封装,应该是在 表现层 进行处理,所以把 结果类 放在 controller包 下
流程:
-
创建 Result 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public class Result {
// 描述统一格式中的数据
private Object data;
// 描述统一格式中的编码,用于区分操作,可以简化配置0或1表示成功失败
private Integer code;
// 描述统一格式中的消息,可选属性
private String msg;
public Result() {
}
// 构造方法是方便对象的创建
public Result(Integer code,Object data) {
this.data = data;
this.code = code;
}
// 构造方法是方便对象的创建
public Result(Integer code, Object data, String msg) {
this.data = data;
this.code = code;
this.msg = msg;
}
// setter...getter...省略
} -
定义返回码 Code 类
1
2
3
4
5
6
7
8
9
10
11
12//状态码
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
}- 注意:code类中的常量设计也不是固定的,可以根据需要自行增减,例如将查询再进行细分为GET_OK、GET_ALL_OK、GET_PAGE_OK等
-
修改 Controller 类的返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42// 统一每一个控制器方法返回值
public class BookController {
private BookService bookService;
public Result save( Book book) {
boolean flag = bookService.save(book);
return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR,flag);
}
public Result update( Book book) {
boolean flag = bookService.update(book);
return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR,flag);
}
public Result delete( Integer id) {
boolean flag = bookService.delete(id);
return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR,flag);
}
public Result getById( Integer id) {
Book book = bookService.getById(id);
Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
String msg = book != null ? "" : "数据查询失败,请重试!";
return new Result(code,book,msg);
}
public Result getAll() {
List<Book> bookList = bookService.getAll();
Integer code = bookList != null ? Code.GET_OK : Code.GET_ERR;
String msg = bookList != null ? "" : "数据查询失败,请重试!";
return new Result(code,bookList,msg);
}
}
统一异常处理
异常的种类及出现异常的原因分类:
- 框架内部 抛出的 异常:因 使用不合规 导致
- 数据层 抛出的 异常:因 外部服务器故障 导致(例如:服务器访问超时)
- 业务层 抛出的 异常:因 业务逻辑书写错误 导致(例如:遍历业务书写操作,导致索引异常等)
- 表现层 抛出的 异常:因 数据收集、校验等规则 导致(例如:不匹配的数据类型间导致异常)
- 工具类 抛出的 异常:因 工具类书写不严谨不够健壮 导致(例如:必要释放的连接长期未释放等)
注意:
- 所有的异常均抛出到 表现层 进行处理(数据层 --> 业务层 --> 表现层)
- 表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,可以用 AOP思想 实现
异常处理器
SpringMVC提供的解决方案,集中的、统一的处理项目中出现的异常

流程:
创建异常处理器类
| 名称 | @RestControllerAdvice |
|---|---|
| 类型 | 类注解 |
| 位置 | Rest风格开发的控制器增强类定义上方 |
| 作用 | 为Rest风格开发的控制器类做增强,标明 这个类是异常处理器 |
| 名称 | @ExceptionHandler |
|---|---|
| 类型 | 方法注解 |
| 位置 | 专用于异常处理的控制器方法上方 |
| 作用 | 设置指定异常的处理方案,功能等同于控制器方法, 出现异常后终止原始控制器执行,并转入当前方法执行,主要就是 用于拦截对应的异常的 |
1 | // @RestControllerAdvice用于 标识当前类为REST风格对应的异常处理器 |
项目中异常处理方案
先对异常进行分类:
-
业务异常(BusinessException)
- 规范的 用户行为 产生的异常
- 用户在页面输入内容的时候未按照指定格式进行数据填写,如在年龄框输入的是字符串
- 不规范 的 用户行为 操作产生的异常
- 如用户故意传递错误数据
- 规范的 用户行为 产生的异常
-
系统异常(SystemException)
- 项目运行过程中 可预计但无法避免 的异常
- 比如 数据库或服务器 宕机
- 项目运行过程中 可预计但无法避免 的异常
-
其他异常(Exception)
- 编程人员 未预期到 的异常,如:用到的文件不存在
具体实现:
自定义异常类
1 | // 自定义异常处理器,用于封装异常信息,对异常进行分类 |
- 让自定义异常类继承
RuntimeException的好处是,后期在抛出这两个异常的时候,就不用在 try…catch… 或 throws 了 - 自定义异常类中添加
code属性的原因是为了更好的区分异常是来自哪个业务的
将 其他异常 包成 自定义异常
1 | public Book getById(Integer id) { |
- 方式一:
try{}catch(){}在 catch 中重新 throw 我们自定义异常即可。 - 方式二:直接 throw 自定义异常即可
处理器类 中 处理自定义异常
1 | // @RestControllerAdvice用于标识当前类为REST风格对应的异常处理器 |
拦截器

- 浏览器发送一个请求会先到Tomcat的web服务器
- Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源
- 如果是静态资源,会直接到Tomcat的项目部署目录下去直接访问
- 如果是动态资源,就需要交给项目的后台代码进行处理
- 在找到具体的方法之前,可以去配置过滤器(可以配置多个),按照顺序进行执行
- 然后进入到到中央处理器(SpringMVC中的内容),SpringMVC会根据配置的规则进行拦截
- 如果满足规则,则进行处理,找到其对应的controller类中的方法进行执行,完成后返回结果
- 如果不满足规则,则不进行处理
拦截器(Interceptor):是一种 动态拦截方法 调用的机制,在 SpringMVC 中 动态拦截控制器 方法的执行
- 作用:
- 在指定的方法调用前后执行预先设定的代码
- 阻止原始方法的执行
- 总结:拦截器就是用来做增强
拦截器 和 过滤器 之间的区别:
- 归属 不同:Filter 属于 Servlet 技术,Interceptor 属于 SpringMVC 技术
- 拦截内容 不同:Filter 对 所有访问 进行增强,Interceptor 仅针对 SpringMVC 的 访问 进行增强

拦截器开发流程
-
创建 拦截器类
- 让 类 实现 HandlerInterceptor 接口,重写接口中的三个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 定义拦截器类,实现HandlerInterceptor接口
// 注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
// 原始方法调用前执行的内容
// 这个是一定执行的
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
// 原始方法调用后执行的内容
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
// 原始方法调用完成后执行的内容
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}- 拦截器类要被 SpringMVC 容器扫描到
- 拦截器中的
preHandler方法,如果返回 true,则代表放行,会执行原始 Controller 类中要请求的方法,如果返回false,则代表拦截,后面的就不会再执行了
-
配置 拦截器类,实现具体的拦截器配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SpringMvcSupport extends WebMvcConfigurationSupport {
private ProjectInterceptor projectInterceptor;
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
protected void addInterceptors(InterceptorRegistry registry) {
//配置拦截器
registry.addInterceptor(projectInterceptor).addPathPatterns("/books" );
}
} -
SpringMVC 添加 SpringMvcSupport 包扫描
1
2
3
4
5
6
public class SpringMvcConfig{
} -
修改 拦截器 拦截规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SpringMvcSupport extends WebMvcConfigurationSupport {
private ProjectInterceptor projectInterceptor;
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
protected void addInterceptors(InterceptorRegistry registry) {
//配置拦截器
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*" );
}
} -
简化 SpringMvcSupport 的编写
- 对标准接口 WebMvcConfigure 简化开发(注意:侵入式较强)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
private ProjectInterceptor projectInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
// 配置多拦截器
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
}
}

拦截器参数
前置处理方法
1 | public boolean preHandle(HttpServletRequest request, |
- 参数
- request:请求对象
- response:响应对象
- handler:被调用的处理器对象,本质上是一个方法对象,对反射中的 Method 对象进行了再包装
- 返回值
- 返回值为 false,被拦截的处理器将不再执行
eg:
1 | // 使用request对象可以获取请求数据中的内容,如获取请求头的 Content-Type |
1 | // 使用handler参数,可以获取方法的相关信息 |
后置处理方法
原始方法运行后运行,如果原始方法被拦截,则不执行
1 | public void postHandle(HttpServletRequest request, |
- modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
完成处理方法
拦截器最后执行的方法,无论原始方法是否执行
1 | public void afterCompletion(HttpServletRequest request, |
- ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
拦截器链的配置
当配置多个拦截器时,形成拦截器链
1 |
|
拦截器执行的顺序 是和 配置顺序 有关。就和前面所提到的运维人员进入机房的案例,先进后出
- 当配置 多个拦截器 时,形成 拦截器链
- 拦截器链 的 运行顺序 参照 拦截器添加顺序 为准
- 当 拦截器 中出现对 原始处理器 的拦截,后面的拦截器均终止运行
- 当拦截器运行中断,仅运行配置在前面的拦截器的 afterCompletion 操作

总结:
- preHandle:与配置顺序相同,必定运行
- postHandle:与配置顺序相反,可能不运行
- afterCompletion:与配置顺序相反,可能不运行
Maven 进阶
依赖 管理
依赖指当前项目运行所需的 jar,一个项目可以设置多个依赖
格式为:
1 | <!--设置当前项目所依赖的所有jar--> |
依赖 具有 传递性

- 依赖传递有 直接依赖 和 间接依赖
- 相对于 A 来说,A 直接依赖 B 和 C ,间接依赖了D1、E1、G 以及 F、D2、E2
- 相对于 B 来说,B 直接依赖了 D1 和 E1,间接依赖了 G
- 直接依赖和间接依赖是一个相对的概念
依赖冲突:是指项目依赖的某一个 jar 包,有多个 不同 的版本,因而造成 类包 版本冲突
- 特殊优先:当 同级 配置了 相同资源 的 不同版本,后配置的覆盖先配置的
- 路径优先:当依赖中出现 相同的资源 时,层级越深,优先级越低,层级越浅,优先级越高(2度 > 3度)
- 声明优先:当资源在 相同层级 被依赖时,配置顺序靠前的覆盖配置顺序靠后的
可选依赖:指 对外隐藏 当前所依赖 的资源(依赖不透明)
1 | <dependency> |
排除依赖:指 主动断开依赖 的资源,被排除的资源无需指定版本(依赖不需要)
1 | <dependency> |
继承 与 聚合
聚合

聚合:将多个模块组织成一个整体,同时进行项目构建的过程称为聚合
聚合工程:通常是一个不具有业务功能的 “空” 工程(有且仅有一个 pom 文件)
作用:使用聚合工程可以将多个工程编组,通过对 聚合工程 进行构建,实现对所包含的模块进行同步构建
- 当工程中某个模块发生更新(变更)时,必须保障工程中与已更新模块关联的模块同步更新,此时可以使用 聚合工程 来解决批量模块同步构建的问题
聚合工程主要是用来管理项目
实现流程:将项目的打包方式改为 pom,并在 pom.xml 添加所要管理的项目
1 |
|
项目的打包方式,分别是
jar:默认情况,说明该项目为 java项目war:说明该项目为 web 项目pom:说明该项目为 聚合或继承 项目
继承

- 继承:描述的是两个工程间的关系(其中一个叫父工程,另外一个叫子工程),与 java 中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。
- 作用:
- 简化配置
- 减少版本冲突
继承 的 实现步骤:
-
创建 Maven 模块,设置打包类型为 pom
1
<packaging>pom</packaging>
-
在 父工程 的 pom 文件中配置依赖关系(子工程将沿用父工程中的依赖关系),一般只抽取子项目中公有的 jar 包
1
2
3
4
5
6
7
8<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
...
</dependencies> -
在父工程中配置子工程中 可选 的 依赖关系
1
2
3
4
5
6
7
8
9
10<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
</dependencies>
...
</dependencyManagement> -
在子工程中配置当前工程所继承的父工程
1
2
3
4
5
6
7
8<!--定义该工程的父工程-->
<parent>
<groupId>com.itheima</groupId>
<artifactId>maven_01_parent</artifactId>
<version>1.0-RELEASE</version>
<!--填写父工程的pom文件,可以不写-->
<relativePath>../maven_01_parent/pom.xml</relativePath>
</parent> -
在子工程中配置使用父工程中可选依赖的坐标
1
2
3
4
5
6<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
</dependencies>
注意事项:
- 子工程中使用父工程中的可选依赖时,仅需要提供群组id和项目id,无需提供版本,版本由父工程统一提供,避免版本冲突
- 子工程中还可以定义父工程中没有定义的依赖关系,只不过不能被父工程进行版本统一管理
聚合 与 继承 的 区别
两种之间的作用:
- 聚合用于 快速构建 项目,对项目进行管理
- 继承用于 快速配置 和 管理子项目 中所使用 jar 包的版本
聚合和继承的 相同点:
- 聚合与继承的 pom.xml 文件打包方式均为 pom,可以将两种关系制作到同一个pom文件中
- 聚合与继承均属于 设计型模块,并无实际的模块内容
聚合和继承的不同点:
- 聚合是在当前模块中配置关系,聚合可以感知到参与聚合的模块有哪些
- 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己
属性
属性的使用
-
父工程 中 定义属性
1
2
3
4
5
6<!--定义属性-->
<properties>
<spring.version>5.2.10.RELEASE</spring.version>
<junit.version>4.12</junit.version>
<mybatis-spring.version>1.3.0</mybatis-spring.version>
</properties> -
修改依赖 的 version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
此时,只需要更新父工程中 properties 标签中所维护的 jar 包版本,所有子项目中的版本也就跟着更新。当然除了将 spring 相关版本进行维护,还可以将其他的 jar 包版本也进行抽取,这样就可以对项目中所有 jar 包的版本进行统一维护
如果项目需要有属性被 父工程 管理,如果有多个项目需要配置,解决办法:
方式一:
1 | <build> |
可以配,但是如果项目够多的话,这个配置也是比较繁琐
方式二:
1 | <build> |
资源文件引用属性
步骤:
-
定义属性
1
2
3
4
5<properties>
<spring.version>5.2.10.RELEASE</spring.version>
<junit.version>4.12</junit.version>
<jdbc.url>jdbc:mysql://127.1.1.1:3306/ssm_db</jdbc.url>
</properties> -
jdbc.properties 文件中引用属性
1
2
3
4jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=${jdbc.url}
jdbc.username=root
jdbc.password=root -
设置 maven 过滤文件范围
1
2
3
4
5
6
7
8
9
10
11
12<build>
<resources>
<!--
${project.basedir}: 当前项目所在目录,子项目继承了父项目,
相当于所有的子项目都添加了资源目录的过滤
-->
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
Maven中的属性分为:
- 自定义属性(常用)
- 内置属性
- Setting 属性
- Java 系统属性
- 环境变量属性

版本管理

- SNAPSHOT(快照版本)
- 项目开发过程中临时输出的版本,称为快照版本
- 快照版本会随着开发的进展不断更新
- RELEASE(发布版本)
- 项目开发到一定阶段里程碑后,向团队外部发布较为稳定的版本,这种版本所对应的构件文件是稳定的
- 即便进行功能的后续开发,也不会改变当前发布版本内容,这种版本称为发布版本
除了上面的工程版本,还会经常能看到一些发布版本:
- alpha版:内测版,bug多不稳定内部版本不断添加新功能
- beta版:公测版,不稳定(比alpha稳定些),bug相对较多不断添加新功能
- 纯数字版
多环境配置与应用
多环境配置:profile

流程:
-
父工程 配置 多个环境,并指定默认激活环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31<profiles>
<!--开发环境-->
<profile>
<id>env_dep</id>
<properties>
<jdbc.url>jdbc:mysql://127.1.1.1:3306/ssm_db</jdbc.url>
</properties>
<!--设定是否为默认启动环境-->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!--生产环境-->
<profile>
<id>env_pro</id>
<properties>
<jdbc.url>jdbc:mysql://127.2.2.2:3306/ssm_db</jdbc.url>
</properties>
</profile>
<!--测试环境-->
<profile>
<id>env_test</id>
<properties>
<jdbc.url>jdbc:mysql://127.3.3.3:3306/ssm_db</jdbc.url>
</properties>
</profile>
</profiles> -
执行安装查看 env_dep 环境是否生效
-
切换默认环境为 生产环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<profiles>
<!--开发环境-->
<profile>
<id>env_dep</id>
<properties>
<jdbc.url>jdbc:mysql://127.1.1.1:3306/ssm_db</jdbc.url>
</properties>
</profile>
<!--生产环境-->
<profile>
<id>env_pro</id>
<properties>
<jdbc.url>jdbc:mysql://127.2.2.2:3306/ssm_db</jdbc.url>
</properties>
<!--设定是否为默认启动环境-->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!--测试环境-->
<profile>
<id>env_test</id>
<properties>
<jdbc.url>jdbc:mysql://127.3.3.3:3306/ssm_db</jdbc.url>
</properties>
</profile>
</profiles> -
执行安装并查看 env_pro 环境是否生效
-
再按照上面步骤测试 测试环境
对于多环境切换只需要两步即可:
-
父工程 中定义 多环境
1
2
3
4
5
6
7
8
9
10
11
12<profiles>
<profile>
<id>环境名称</id>
<properties>
<key>value</key>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
...
</profiles> -
使用多环境(构建过程)
1
2
3// maven指令按钮
mvn 指令 -P 环境定义ID[环境定义中获取]
跳过测试
方式一:IDEA工具实现跳过测试

图中的按钮为Toggle 'Skip Tests' Mode,翻译为切换的意思,也就是说在 测试与不测试 之间进行切换。说明测试已经被关闭,再次点击就会恢复。
方式二:配置插件实现跳过测试
在父工程中的 pom.xml 中添加测试插件配置
1 | <build> |
- skipTests:如果为true,则跳过所有测试,如果为false,则不跳过测试
- excludes:哪些测试类不参与测试,即排除,针对skipTests为false来设置的
- includes: 哪些测试类要参与测试,即包含,针对skipTests为true来设置的
私服

私服:公司内部搭建的用于存储Maven资源的服务器
远程仓库:Maven开发团队维护的用于存储Maven资源的服务器
也就是说,私服是一台独立的服务器,用于 解决团队内部的资源共享与资源同步 问题
搭建Maven私服的方式有很多,介绍其中一种使用量比较大的实现方式:
- Nexus
- Sonatype 公司的一款 maven 私服产品
- 下载地址:https://help.sonatype.com/repomanager3/download

所有私服仓库总共分为三大类:
-
宿主仓库(hosted)
- 保存无法从中央仓库获取的资源
- 自主研发
- 第三方非开源项目,比如:Oracle,因为是付费产品,所以中央仓库没有
- 保存无法从中央仓库获取的资源
-
代理仓库(proxy)
- 代理远程仓库,通过nexus访问其他公共仓库,例如中央仓库
-
仓库组(group)
- 将若干个仓库组成一个群组,简化配置
- 仓库组不能保存资源,属于设计型仓库

SpringBoot
SpringBoot 概述
SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目的是用来 简化 Spring 应用的 初始搭建 以及 开发过程
SpringBoot的官网:https://spring.io/projects/spring-boot

-
坐标
Spring程序中的坐标需要自己编写,而且坐标非常多SpringBoot程序中的坐标是在创建工程时进行勾选自动生成的 -
web3.0配置类
Spring程序需要自己编写这个配置类。SpringBoot程序不需要自己书写 -
配置类
Spring/SpringMVC程序的配置类需要自己书写。而SpringBoot程序则不需要书写。
注意:基于 Idea 的 Spring Initializr 快速构建 SpringBoot 工程时需要联网
SpringBoot 工程 的 打包:通过 Maven 的 package 指令打包就会在 target 目录下生成对应的 jar 包,同时在构建 SpringBoot 工程时已经在 pom.xml 中配置了如下插件:
1 | <plugin> |
SpringBoot 工程 的 启动:进入 jar 包所在位置,在 命令提示符 中输入如下命令:
1 | java -jar xxx.jar |
SpringBoot 程序优点 恰巧就是针对 Spring 的缺点
- 自动配置(简化依赖配置)
- 这个是用来解决
Spring程序配置繁琐的问题
- 这个是用来解决
- 起步依赖
- 这个是用来解决
Spring程序依赖设置繁琐的问题
- 这个是用来解决
- 辅助功能(内置服务器, …)
- 在启动
SpringBoot程序时既没有使用本地的tomcat也没有使用tomcat插件,而是使用SpringBoot内置的服务器
- 在启动
首先看 SpringBoot 的 pom.xml 依赖:
1 |
|
起步依赖(starter):SpringBoot 中常见项目名称,定义了当前项目使用的所有项目坐标,以达到减少依赖配置的目的,以后需要用到什么技术,直接导入对应的 起步依赖 即可,里面用到的各种依赖的版本号都已经配置好了。
spring-boot-starter-parent(parent)
- 所有
SpringBoot项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到 减少依赖冲突 的目的 spring-boot-starter-parent(2.5.0)与spring-boot-starter-parent(2.4.6)共计57处坐标版本不同
实际开发
-
使用任意坐标时,仅书写 GAV 中的 G 和 A ,V 由 SpringBoot 提供
G:groupid
A:artifactId
V:version
-
如发生坐标错误,再指定 version(要小心版本冲突)
程序启动:
创建的每一个 SpringBoot 程序时都包含一个类似于下面的类,将这个类称作 引导类
1 |
|
注意:
-
SpringBoot在创建项目时,采用jar的打包方式 -
SpringBoot的 引导类 是项目的入口,运行main方法就可以启动项目- 因为我们在
pom.xml中配置了spring-boot-starter-web依赖,而该依赖是依赖tomcat,所以运行main方法就可以使用tomcat启动工程
- 因为我们在
现在启动工程使用的是 tomcat 服务器,那能不能不使用 tomcat 而使用 jetty 服务器,需要导入下面的starter 依赖
1 | <dependency> |
- Jetty 比 Tomcat 更轻量级,可扩展性更强(相较于Tomcat),谷歌应用引擎(GAE)已经全面切换为Jetty
基础配置
配置文件格式
SpringBoot 提供了多种属性 配置方式
-
application.properties1
server.port=80
-
application.yml1
2server:
port: 81 -
application.yaml1
2server:
port: 82
注意:
SpringBoot程序的配置文件名必须是application,只是 后缀名不同 而已
启动服务,在控制台可以看到使用的端口号是 80。说明 application.properties 的优先级最高
注释掉 application.properties 配置文件内容。再次启动服务,在控制台可以看到使用的端口号是 81,说明 application.yml 配置文件为第二优先级。
从上述的验证结果可以确定 三种配置文件 的 优先级 是:
application.properties > application.yml > application.yaml
注意:
SpringBoot核心配置文件名为application
SpringBoot内置属性过多,且所有属性集中在一起修改,在使用时,通过提示键 + 关键字修改属性例如:要设置日志的级别时,可以在配置文件中书写
logging,就会提示出来。配置内容如下
1
2
3 logging:
level:
root: info
yaml 格式
**YAML(YAML Ain’t Markup Language),一种数据序列化格式。**这种格式的配置文件在近些年已经占有主导地位,那么这种配置文件和前期使用的配置文件是有一些优势的,下面是一些数据格式的比对:
最开始我们使用的是 xml ,格式如下:
1 | <enterprise> |
而 properties 类型的配置文件如下:
1 | enterprise.name=itcast |
yaml 类型的配置文件内容如下:
1 | enterprise: |
优点:
-
容易阅读
yaml类型的配置文件比xml类型的配置文件更容易阅读,结构更加清晰 -
容易 与脚本语言交互
-
以 数据 为核心,重数 据轻格式
yaml更注重数据,而xml更注重格式
YAML 文件扩展名:
.yml(主流).yaml
上面两种后缀名都可以,以后使用更多的还是 yml 的
yml的语法格式:
-
大小写 敏感
-
属性层级关系使用多行描述,每行结尾使用 冒号 结束
-
使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键),空格的个数并不重要,只要保证同层级的左侧对齐即可
-
属性值前面添加空格(属性名与属性值之间使用 冒号 + 空格 作为分隔)
-
#表示 注释
核心规则:数据前面要加 空格 与 冒号 隔开
数组数据在数据书写位置的下方使用 减号 作为数据开始符号,每行书写一个数据,减号与数据间空格分隔,例如:
1 | enterprise: |
yaml 数据读取
@Value(直接读取)
使用 @Value("表达式") 注解可以从 配合文件 中读取数据
注解中用于读取属性名引用方式是:${一级属性名.二级属性名……}
eg:
1 |
|
Environment(封装后读取)
上面方式读取到的数据特别零散,SpringBoot 还可以使用 @Autowired 注解注入 Environment 对象的方式读取数据
这种方式 SpringBoot 会将配置文件中所有的数据封装到 Environment 对象中,如果需要使用哪个数据只需要通过调用 Environment 对象的 getProperty(String name) 方法获取
eg:
1 |
|
注意:这种方式,框架内容大量数据,而在 开发中 很少使用
实体类封装属性(封装后读取)
SpringBoot 还提供了将配置文件中的数据封装到 自定义的实体类对象 中的方式。具体操作如下:
-
将实体类
bean的创建交给Spring管理。- 在类上添加
@Component注解
- 在类上添加
-
使用
@ConfigurationProperties注解表示加载配置文件- 在该注解中也可以使用
prefix属性指定只加载指定前缀的数据
- 在该注解中也可以使用
-
在
BookController中进行注入
eg:
Enterprise 实体类内容如下:
1 |
|
BookController 内容如下:
1 |
|
这种方式会 报警告,解决办法:
1 | <dependency> |
多环境配置
application.yml 配置文件内容如下:
1 | # 设置启用的环境 |
最新的写法:
1 | # 开发环境 |
SpringBoot 定义了配置文件不同的放置的位置,而放在不同位置的优先级时不同的。
SpringBoot 中4级配置文件放置位置:
- 1级:classpath(类路径下):application.yml (最低)
- 2级:classpath:config/application.yml
- 3级:file (文件路径下):application.yml
- 4级:file :config/application.yml (最高)
说明:
- 级别越高 优先级越高
- 1级 与 2级 用作系统开发阶段设置通用属性
- 3级 与 4级 留作系统打包后设置通用属性
SpringBoot 整合 junit
对比 Spring 整合 junit
1 | // 设置运行器 |
使用 @RunWith 注解指定运行器,使用 @ContextConfiguration 注解来指定配置类或者配置文件
而 SpringBoot 整合 junit 特别简单,分为以下三步完成:
- 在测试类上添加
SpringBootTest注解 - 使用
@Autowired注入要测试的资源 - 定义测试方法进行测试
在 test/java 下创建 com.itheima 包,在该包下创建测试类,将 BookService 注入到该测试类中
eg:
1 |
|
注意:这里的引导类所在包 必须是 测试类所在包 及其 子包。
例如:
- 引导类所在包是
com.itheima- 测试类所在包是
com.itheima如果不满足这个要求的话,就需要在使用
@SpringBootTest注解时,使用classes属性指定引导类的字节码对象。如@SpringBootTest(classes = Springboot07TestApplication.class)
SpringBoot 整合 Mybatis
对比 Spring 整合 Mybatis 需要定义很多配置类
-
SpringConfig配置类-
导入
JdbcConfig配置类 -
导入
MybatisConfig配置类1
2
3
4
5
6
7
public class SpringConfig {
}
-
-
JdbcConfig配置类-
定义数据源(加载 properties 配置项:driver、url、username、password)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class JdbcConfig {
private String driver;
private String url;
private String userName;
private String password;
public DataSource getDataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
-
-
MybatisConfig配置类-
定义
SqlSessionFactoryBean -
定义映射配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public MapperScannerConfigurer getMapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.itheima.domain");
ssfb.setDataSource(dataSource);
return ssfb;
}
-
而 SpringBoot 整合 Mybatis 的流程:
-
定义实体类
1
2
3
4
5
6
7
8
9
10public class Book {
private Integer id;
private String name;
private String type;
private String description;
//setter and getter
//toString
} -
定义dao接口
1
2
3
4
5
public interface BookDao {
public Book getById(Integer id);
} -
使用 Druid 数据源
1
2
3
4
5<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency> -
在
application.yml配置文件配置1
2
3
4
5
6
7spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource- 注意:
SpringBoot版本低于2.4.3(不含),Mysql驱动版本大于8.0时,需要在 url 连接串中配置时区jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC,或在MySQL数据库端配置时区解决此问题
- 注意:






