1 Maven
依赖传递性
路径最短者优先【就近原则】
先声明者优先
2 Spring Framework
概述
IOC:“控制反转”,指把创建对象过程交给 Spring 进行管理。
AOP:“面向切面编程”,减少系统的重复代码,降低模块间的耦合度。
IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。
spring的依赖:spring-context
通过反射机制调用无参数构造方法创建对象
spring源码底层就是一个map集合
log4j依赖
1 | <!--log4j2的依赖--> |
IoC容器在Spring的实现
BeanFactory,这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。
ApplicationContext,BeanFactory 的子接口,面向 Spring 的使用者
基于XML管理Bean
获取bean:根据id获取,根据类型获取,根据id和类型
依赖注入
setter注入,构造器注入
对象类型属性赋值
方式一:引用外部bean
1 | <!-- ref属性:引用IOC容器中某个bean的id,将所对应的bean为属性赋值 --> |
方式二:内部bean
方式三:级联属性赋值
为数组类型属性赋值
为集合类型属性赋值(List,Map)
引用集合类型的bean
bean的作用域
scope属性:取值singleton(默认值)
scope属性:取值prototype,bean在IOC容器中可以有多个实例
生命周期过程
bean对象创建(调用无参构造器)
给bean对象设置属性
bean的后置处理器(初始化之前)
bean对象初始化(需在配置bean时指定初始化方法)
bean的后置处理器(初始化之后)
bean对象就绪可以使用
bean对象销毁(需在配置bean时指定销毁方法)
IOC容器关闭
bean的后置处理器
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行
FactoryBean
配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。
基于xml自动装配
autowire=”byType”
autowire=”byName”
组件扫描的一些配置
情况二:指定要排除的组件
情况三:仅扫描指定组件
@Autowired注入
默认根据类型装配。【默认是byType】
属性注入
@Autowired
private UserDao userDao;
set注入
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
构造方法注入
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
形参上注入
public UserServiceImpl(@Autowired UserDao userDao) {
this.userDao = userDao;
}
当有参数的构造方法只有一个时,@Autowired注解可以省略。
场景六:@Autowired注解和@Qualifier注解联合
@Resource注解
@Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。- @Autowired注解是Spring框架自己的。
Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖
1 | <dependency> |
@Resource注解用在属性上、setter方法上。
@Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
动态代理和静态代理
AOP概念
1 横切关注点——要解决的问题,如,要解决日志管理的问题
2 通知(增强)——就是你想要增强的功能,比如日志
- 前置通知:在被代理的目标方法前执行
- 返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
- 异常通知:在被代理的目标方法异常结束后执行(死于非命)
- 后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
- 环绕通知:使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置
后置通知(@After)总是在返回通知(@AfterReturning)之后执行,无论方法是否成功完成。
3 切面——封装通知方法的类
4 目标——被代理的目标对象。
5 代理——向目标对象应用通知之后创建的代理对象。
6 连接点——spring允许你使用通知的地方
AOP
动态代理分为JDK动态代理和cglib动态代理,当目标类没有接口时只能使用cglib动态代理,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口`
JDK动态代理动态生成的代理类会在com.sun.proxy包下,类名为$proxy1,和目标类实现相同的接口cglib动态代理动态生成的代理类会和目标在在相同的包下,会继承目标类,所以不需要目标类实现接口。
AspectJ:
是AOP思想的一种实现。本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。`
依赖
1 | <!--spring aop依赖--> |
步骤
1 创建切面类并配置

重用切入点表达式,@Pointcut注解
①声明
1 |
|
②在同一个切面中使用
@Pointcut注解后+方法名
1 |
|
获取连接点信息
1 | //获取连接点的签名信息 |
②获取目标方法的返回值
@AfterReturning中的属性returning,用来将通知方法的某个形参,接收目标方法的返回值
1 | @AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result") |
③获取目标方法的异常
@AfterThrowing中的属性throwing,用来将通知方法的某个形参,接收目标方法的异常
1 |
|
使用@Order注解可以控制切面的优先级:
junit5
依赖
1 | <!--spring对junit的支持相关依赖--> |
整合
1 | //两种方式均可 |
而junit4
1 |
|
junit4和5有什么区别?
声明式事务
添加注解@Transactional
@Transactional标识在方法上,则只会影响该方法
@Transactional标识的类上,则会影响类中所有的方法
事务属性
事务属性:只读
(readOnly = true)
事务属性:超时
超时时间单位秒
(timeout = 3)
回滚策略
rollbackFor属性:需要设置一个Class类型的对象
rollbackForClassName属性:需要设置一个字符串类型的全类名
noRollbackFor属性:需要设置一个Class类型的对象
rollbackFor属性:需要设置一个字符串类型的全类名
隔离级别
1 | @Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别 |
事务的传播行为
什么是事务的传播行为?
在service类中有a()方法和b()方法,a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为。
一共有七种传播行为:
- REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行**【有就加入,没有就不管了】**
- MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常**【有就加入,没有就抛异常】**
- REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起**【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】**
- NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务**【不支持事务,存在就挂起】**
- NEVER:以非事务方式运行,如果有事务存在,抛出异常**【不支持事务,存在就抛异常】**
- NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】
@Bean是方法级别注解,用于显式定义bean
@Component是类级别注解,用于自动检测和注册bean
资源操作:Resources
数据校验:Validation
提前编译:AOT
JIT, Just-in-time,动态(即时)编译,边运行边编译;
AOT,Ahead Of Time,指运行前编译,预先编译。
云原生破局利器
3. SpringMVC
SpringMVC涉及组件理解:
- DispatcherServlet : 整个流程处理的核心,所有请求都经过它的处理和分发![ CEO ]
- HandlerMapping : 它内部缓存handler(controller方法)和handler访问路径数据,被DispatcherServlet调用,用于查找路径对应的handler![秘书]
- HandlerAdapter : 它可以处理请求参数和处理响应数据数据,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handler和DispatcherServlet之间的适配器![经理]
- Handler : handler又称处理器,他是Controller类内部的具体方法的简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果![打工人]
- ViewResovler : 视图解析器,主要作用简化模版视图页面查找的,后端只返回JSON数据,不返回页面,那就不需要视图解析器!所以,相对其他的组件不是必须的![财务]
Spring MVC核心组件配置类
可以不在配置类里添加RequestMappingHandlerMapping和RequestMappingHandlerAdapter,springmvc会检查是否配置handlerMapping和handlerAdapter,没有配置默认加载spring-webmvc包下的配置
@ImportResource(“classpath:spring-mvc.xml”) 注解
1 | <servlet-mapping> |
SpringMVC接收数据
模糊路径匹配
/product/*
/* 为单层任意字符串,/** 为任意层任意字符串
HTTP 协议定义了八种请求方式,在 SpringMVC 中封装到了RequestMethod枚举类
违背请求方式,会出现405异常!!!
8种请求方式
1 | public enum RequestMethod { |
进阶注解只能添加到handler方法上,无法添加到类上!
4 接收参数
参数编码
param 类型的参数会被编码为 ASCII 码,JSON 类型的参数会被编码为 UTF-8
参数顺序:
param 类型的参数没有顺序限制。但是,
JSON 类型的参数是有序的。JSON 采用键值对的形式进行传递,其中键值对是有序排列的。数据类型:
param 类型的参数仅支持字符串类型、数值类型和布尔类型等简单数据类型。而 JSON 类型的参数则支持更复杂的数据类型,如数组、对象等。可读性:
param 类型的参数格式比 JSON 类型的参数更加简单、易读。但是,JSON 格式在传递嵌套数据结构时更加清晰易懂。
1 param参数接收
只要形参参数名和类型与传递参数相同,即可自动接收!
@RequestParam还可以为请求参数提供默认值
实体接收
2 路径参数接收
@PathVariable
1 | getUser( Long id, |
3 json参数接收
1 |
|
@EnableWebMvc,json数据处理,必须使用此注解,因为他会加入json处理器
1 | @EnableWebMvc |
jackson依赖
1 | <dependency> |
4 接收cookie
(@CookieValue(“JSESSIONID”) String cookie)
原生Api对象操作
还有model,map,moedlmap,三个对象没讲,下一章节讲,都是request级别的作用域,放在形参列表,另一个request级别的:ModelAndView,放在方法区。
共享域对象操作
Request级别属性(共享)域
Model 类型
1 |
|
ModelMap 类型
Map 类型
1 | @RequestMapping("/attr/request/map") |
原生 request
5 ModelAndView 对象
session级别
HttpSession session
Application级别属性(共享)域
1 | servletContext.setAttribute("appScopeMsg", "i am hungry..."); |
SpringMVC响应数据
3.2.2 转发和重定向
返回JSON数据(重点)
导入jackson依赖
1 | <dependency> |
四,RESTFul风格设计和实战
客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词
URI(统一资源标识符)或者一个 URL(统一资源定位符)。
使用习惯
路径参数应该用于指定资源的唯一标识或者 ID,而请求参数应该用于指定查询条件或者操作参数。
对于敏感信息,最好使用 POST 和请求体来传递参数。
@PatchMapping更新局部,@PutMapping更新整体
五、SpringMVC其他扩展
全局异常处理机制
编程式异常处理 X
声明式异常处理 √
标注相应的注解(如 @Throws 或 @ExceptionHandler)
基于注解声明异常处理
- 声明异常处理控制器类
1 | @RestControllerAdvice |
- 声明异常处理hander方法
@ExceptionHandler(HttpMessageNotReadableException.class)
该注解标记异常处理Handler,并且指定发生异常调用该方法!
1 |
|
3.配置文件扫描控制器类配置
确保异常处理控制类被扫描
拦截器
拦截器 Springmvc VS 过滤器 javaWeb:
springmvc怎么使用拦截器?
- 创建拦截器类
public class Process01Interceptor implements HandlerInterceptor
- 修改配置类添加拦截器
在配置类中添加
1 | //添加拦截器 |
参数校验
spring中也有参数校验?
配置 @EnableWebMvc后,SpringMVC 会默认装配好一个 LocalValidatorFactoryBean。启用数据校验。Spring 的 LocalValidatorFactoryBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口
依赖
1 | <!-- 校验注解 --> |
常用的
1 | @Min(10) |
易混总结
@NotNull、@NotEmpty、@NotBlank 都是用于在数据校验中检查字段值是否为空的注解,但是它们的用法和校验规则有所不同。
@NotNull (包装类型不为null)
@NotNull 注解是 JSR 303 规范中定义的注解,当被标注的字段值为 null 时,会认为校验失败而抛出异常。该注解不能用于字符串类型的校验,若要对字符串进行校验,应该使用 @NotBlank 或 @NotEmpty 注解。
@NotEmpty (集合类型长度大于0)
@NotEmpty 注解同样是 JSR 303 规范中定义的注解,对于 CharSequence、Collection、Map 或者数组对象类型的属性进行校验,校验时会检查该属性是否为 Null 或者 size()==0,如果是的话就会校验失败。但是对于其他类型的属性,该注解无效。需要注意的是只校验空格前后的字符串,如果该字符串中间只有空格,不会被认为是空字符串,校验不会失败。
@NotBlank (字符串,不为null,切不为” “字符串)
@NotBlank 注解是 Hibernate Validator 附加的注解,对于字符串类型的属性进行校验,校验时会检查该属性是否为 Null 或 “” 或者只包含空格,如果是的话就会校验失败。需要注意的是,@NotBlank 注解只能用于字符串类型的校验。
总之,这三种注解都是用于校验字段值是否为空的注解,但是其校验规则和用法有所不同。在进行数据校验时,需要根据具体情况选择合适的注解进行校验。
六、SpringMVC总结
| 核心点 | 掌握目标 |
|---|---|
| springmvc框架 | 主要作用、核心组件、调用流程 |
| 简化参数接收 | 路径设计、参数接收、请求头接收、cookie接收 |
| 简化数据响应 | 模板页面、转发和重定向、JSON数据、静态资源 |
| restful风格设计 | 主要作用、具体规范、请求方式和请求参数选择 |
| 功能扩展 | 全局异常处理、拦截器、参数校验注解 |
4. Mybatis
mybatis的mysql依赖
1 | <!-- MySQL驱动 mybatis底层依赖jdbc驱动实现,本次不需要导入连接池,mybatis自带! --> |

1 | <!-- namespace等于mapper接口类的全限定名,这样实现对应 --> |
注意:
- 方法名和SQL的id一致
- 方法返回值类型和resultType一致
- 方法的参数和SQL的参数一致
- 接口的全类名和映射配置文件的名称空间一致
数据库部分:
之所以写成:emp_id empId,emp_name empName, emp_salary empSalary,是为了数据库字段和实体形成映射(一致)

log4j2依赖
1 | <!--log4j2的依赖--> |
或logback依赖
1 | <!-- 日志 , 会自动传递slf4j门面--> |
日志配置
1 | <settings> |
传参
#{}形式
Mybatis会将SQL语句中的#{}转换为问号占位符。
${}形式
${}形式传参,底层Mybatis做的是字符串拼接操作。
通常不会采用${}的方式传值。一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,(对于表名、列名等数据库对象的部分,JDBC 预编译语句是不支持使用问号占位符的,这是因为数据库引擎在解释 SQL 语句时需要确定对象的名称,而这些对象名称通常不能通过占位符动态替换)此时只能使用${}
特殊情况: 动态的不是值,是列名或者关键字,需要使用${}拼接
//注解方式传入参数!!
1 |
|

表层现象:Mybatis能获取传入的实体类型的属性,也就是传实体,相当于传了实体的所有属性
实际原因:Mybatis会根据#{}中传入的数据,加工成getXxx()方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}解析后的问号占位符这个位置。
零散的多个简单类型参数,如果没有特殊处理,那么Mybatis无法识别自定义名称

1 | int updateEmployee(@Param("empId") Integer empId,@Param("empSalary") Double empSalary); |
还可以传Map类型参数
有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。
数据输出:
resultType = “全限定符 | 别名 | 如果是返回集合类型,写范型类型即可"
可设置类型别名
其一:
1 | <typeAliases> |
其二:
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
1 | <typeAliases> <package name="domain.blog"/> </typeAliases> |
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子
1 |
|
驼峰映射
1 | <!-- 在全局范围内对Mybatis进行配置 --> |
返回Map类型
Mapper接口的抽象方法
1 | Map<String,Object> selectEmpNameAndMaxSalary(); |
SQL语句
1 | <!-- Map<String,Object> selectEmpNameAndMaxSalary(); --> |
junit测试
1 |
|
//TODO有个爆红,mapkey is required
返回主键值
是将自增主键的值设置到实体类对象中
1 | <insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId"> |
非自增长类型主键
而对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素。selectKey 元素将会首先运行,id 会被设置,然后插入语句时会被调用!
使用 selectKey 帮助插入UUID作为字符串类型主键示例:
1 | <insert id="insertUser" parameterType="User"> |
parameterType指定插入的java对象
通过 keyProperty 属性来指定查询到的 UUID 赋值给对象中的 id 属性,而 resultType 属性指定了 UUID 的类型为 java.lang.String。
1 | <selectKey keyProperty="id" resultType="java.lang.String" |
实体类属性和数据库字段对应关系(3种)
使用resultMap
1 | <resultMap id="selectEmployeeByRMResultMap" type="com.this0.pojo.Employee"> |
mybatis配置文件
1 | <configuration> |
junit
1 | @BeforeEach |
@BeforeEach
select标签
timeout和statementType基本不用
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
insert标签
keyProperty=”指定主键在实体类对象中对应的属性名”
keyColumn=”设置生成键值在表中的列名”
希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样
实体类设计方案
对一关系下,类中只要包含对方单个对象类型属性即可!
对多关系下,类中只要包含对方类型集合属性即可!
只有真实发生多表查询时,才需要设计和修改实体类,否则不提前设计和修改实体类!
一般是功能开发完成,再加外键约束检查是否有bug。
多对一映射!多个属性放在一个实体里
举例
1 | <!-- 创建resultMap实现“对一”关联关系映射 --> |
在“对一”关联关系中,我们的配置比较多,但是关键词就只有:association和javaType
1对多映射
1 | <!-- 配置resultMap实现从Customer到OrderList的“对多”关联关系 --> |
在“对多”关联关系中,同样有很多配置,但是提炼出来最关键的就是:“collection”和“ofType”
多对多映射,使用中间表
多表映射优化
1 | <!--开启resultMap自动映射 --> |
四,动态语句
if和where标签
where标签会自动去掉标签体内,前面多余的and/or
set标签
使用set标签动态管理set子句,并且动态去掉两端多余的逗号`
trim标签
trim标签控制条件部分两端是否包含某些字符`
- prefix属性:指定要动态添加的前缀
- suffix属性:指定要动态添加的后缀
- prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
- suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
choose/when/otherwise标签
在多个分支条件中,仅执行一个。
类似于switch case语句
- 从上到下依次执行条件判断
- 遇到的第一个满足条件的分支会被采纳
- 被采纳分支后面的分支都将不被考虑
- 如果所有的when分支都不满足,那么就执行otherwise分支
foreach标签
用于遍历
1 | <!-- |
需要额外配置
1 | dev.url=jdbc:mysql:///mybatis-example?allowMultiQueries=true |
五,高级拓展
Mapper批量映射优化
1 | <mappers> |
分页插件
1.引入依赖
2.配置
1 | <plugins> |
3.使用



