JavaWeb
简单请求参数
http://localhost:8080/xxx?name=1&age=2
1 |
|
1 |
|
如果参数名不匹配,则使用@RequestParam建立映射
1 |
|
将参数封装到实体类
1 |
|
接收数组参数
http://localhost:8080/xxx?hobby=1&hobby=2&hobby=3
1 |
|
1 |
|
日期参数
http://localhost:8080/xxx?updateTime-2022-12-12 10:05:45
1 |
|
Json参数
json数据键名与形参属姓名相同,定义pojo类型即可接收参数,需要使用@RequesBody标示
1 | { |
1 |
|
路径参数
通过请求URL直接换地参数用{}来识别路径参数,需要使用@PathVariable来获取路径参数
http://localhost:8080/arrayParam5/1
1 | @RequestMapping("/arrayParam5/{id}") |
http://localhost:8080/arrayParam5/1/2
@RequestMapping(“/arrayParam5/{id}/{name}”)
public String arrayParam5(@PathVariable Integer id,@PathVariable Integer name){
System.out.println(id+name);
return "ok";
}
相应数据
都来源于注解@ResponseBody只不过@RestController中已经包含
返回字符串就直接但会
实体类对象会转json
数组依然是数组
统一都响应结果
1 | public class Result{ |
分层解耦
分层
三层架构:数据访问(Dao)、逻辑处理(Service)、接收请求响应数据(Controller)
- controller:控制层,,接收前端发送到请求,对请求进行处理,并响应数据。
- service:业务逻辑层,处理具体的业务逻辑
- dao:数据访问层(Data Access Object)(持久层),负责数据访问操作,包括增删改查
解藕
- 内聚:软件中各个功能模块内部的功能联系
- 耦合:衡量软件中各个层/模块之间的依赖、关联的层度。
- 软件设计原则:高内聚低耦合
控制反转:IOC,对象的创建控制权由程序自身转移到外部(容器)
依赖注入:DI,容器为应用程序提供运行时,所依赖的资源
Bean对象:IOC容器中传教、管理的对象
IOC&DI入门
控制反转
@Component 将当前类交给IOC容器,成为IOC容器中的bean
依赖注入
@Autowired 运行时,IOC容器会提供该类型的bean对象,并复制给该变量
IOC详解
@Componet | 声明bean的基础注解 |
---|---|
@Controller | 标注在控制器类上 |
@Service | 标注在业务类上 |
@Repository | 标注在数据访问类上(由于mybiatis整合,用到少) |
Bean默认名字为首字母小写的类名
如果需要改名字可以在注解后面加括号设置
@Repository(“daoA”)
@SpringBootApplication默认包含扫描器范围为当前包和子包
@ComponentScan为扫描
@ComponentScan({“dao”,”com.alan”})指定扫描那个包(不推荐)
DI详解
因为默认是按类型进行的,如果存在多个相同类型的bean会报错
通常使用一下几种方案来解决
- @Primary 优先级
- @Qualifier 指定需要注入对应名字的bean
- @Resource 指定需要注入对应名字的bean,且不需要autowired注解了
Mysql
关系型数据库(RDBMS)建立在关系模型基础上,由多张互相连接的二维表组成的数据库
Sql
- 可以单行或多行书写,以分号结尾
- 可以使用空格/缩进来增强可读性
- 不区分大小写
- 注释
- 单行:–
- 多行/* */
分类
分类 | 说明 |
---|---|
DDL | 数据定义语言,定义数据库对象(数据库,表,字段) |
DMl | 数据操作语言,对数据库表中的数据CUDA |
DQL | 数据查询语言,用来查询数据库表中的记录 |
DCL | 数据库控制语言,用来创建数据库用户、控制数据库的访问权限 |
需求+原型
设计
概要设计、详细设计、接口设计、数据库设计
Java程序
优化->数据库优化
DDL
**数据库 **
查询
1 | 查询所有数据库:show databases; |
1 | 查询当前数据库: selsect database(); |
使用
1 | 使用数据库:use 数据库名; |
创建
1 | 创建数据库:create database [if not exists] 数据库名; |
删除
1 | 删除数据库: drop database [if exists] 数据库名; |
表
[]可选
创建
1 | create table 表名( |
1 | create table tb_user( |
约束
概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据
目的:保证数据库中数据的正确性,有效性,完整性
约束 | 描述 | 关键字 |
---|---|---|
非空约束 | 限制改字段值不能为null | not null |
唯一约束 | 保证字段的所有数据都是唯一,不重复的 | unique |
主键约束 | 主键是一行数据的唯一表示,要求非空且唯一,在后面添加auto_increment自动增加 | primary key |
默认约束 | 保存数据时,如果未指定该字段值,则采用默认值 | default |
外键约束 | 让两张表的数据建立连接,保证数据的一直性和完整性 | Foreign key |
数据类型:查表
表结构查询
- 查询当前数据库下所有表:show tables;
- 查询表结构: desc 表名;
- 查询建表语句: show create table 表名;
修改表结构
- 添加字段: alter table 表名 add 字段名 类型(长度) [comment 注释] [约束];
- 修改字段类型: alter table 表名 modify 字段名 新数据类型(长度);
- 修改字段名和字段类型: alter table 表名 change 旧字段名 新字段名 类型(长度) [comment 注释] [约束]
- 删除字段: alter table 表名 drop column 字段名;
- 修改表名: rename table 表名 to 新表名;
- 删除表: drop table if exists 表名;
在删除表结构时,数据也会被删除
DML
插入数据
- 指定字段添加数据: insert into 表名 (字段1,字段2) values (值1,值2);
- 全部字段添加数据: insert into 表名 values (值1,值2,…);
- 批量添加数据(指定字段): insert into 表名 (字段名1,字段名2) values (值1,值2),(值1,值2);
- 批量添加数据(全部字段):insert into 表名 values (值1,值2…),(值1,值2…);
更新时间可以使用now();
前面有多少个字段,有面就需要多少个值
字符串和日期需要用引号阔起来
插入的数据大小应该在字段限制之内
更新数据
- 修改数据: update 表名 set 字段名1=值1,字段名2=值2…. [where 条件];
没有where会更新整张表
删除
- 删除数据: delete from 表名 [where 条件];
没有条件会清空整张表的数据
不能删除某一个字段的值,如果要操作使用update设置为null
DLQ
用来查询数据库中的记录Select
1 | select |
基本查询
- 查询多个字段:select 字段1,字段2 from 表名;
- 查询所有字段: select * from 表名;
- 设置别名: select 字段1 [as 别名1] from 表名;
- 去除重复记录: select distinct 字段列表 from 表名;
条件查询
- 条件查询: select 字段列表 from 表名 where 条件列表;
比较运算符 | 功能 |
---|---|
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
= | 等于 |
<>或!= | 不等于 |
between … and … | 在某个范围之内(含最小,最大值) |
in(…) | 在in之后的列表中的值,多选一 |
like 占位符 | 模糊匹配使用通配符’_’单个字符‘%’任意字符 |
is null | 是null |
逻辑运算符 | 功能 |
---|---|
and或&& | 并且 |
or或|| | 或者 |
not或| | 非 |
select * from tb_tmp where name=’杨逍’;
select * from tb_tmp where id <= 5;
select * from tb_tmp where job is null;
select * from tb_tmp where job is not null;
select * from tb_tmp where password != ‘123456’;
select * from tb_tmp where entry data >= ‘2000-01-01’ and data <=’2010-01-01’;
select * from tb_tmp where between ‘2000-01-01’ and ‘2010-01-01’;
select * from tb_tmp where between ‘2000-01-01’ and ‘2010-01-01’ and gender =2;
select * from tb_tmp where job = 2 or job = 3 or job= 4;
select * from tb_tmp where job in (2,3,4) ;
select * from tb_tmp where name like ‘__’;
select * from tb_tmp where name like ‘张%’;
分组查询
函数 | 功能 |
---|---|
count | 统计数量 |
max | 最大值 |
min | 最小值 |
avg | 平均值 |
sum | 求和 |
语法:select 聚合函数(字段列表) from 表名;
统计企业员工数量
Select count(id) from tb_emp;//不对null记述
Select count(0) from tb_emp;//不对null记述
Select count(*) from tb_emp;//不对null记述
推荐count(*)
最早入职的员工
select min(entrydate) from tb_emp;
最迟入职员工
select max(entry date) from tb_emp;
统计某列的平均值
select avg(xx) from table;
求和
select sum(xx) from table;
分组查询
只能返回: 分组字段或聚合函数
语法:select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后的条件];
根据性别分组,统计男生于女生的数量
select gender,count(*) from tb_emp group by gender;
先查询入职之间在xxx-xxx 以前的员工,并结果根据职位分组,获取员工数量大于等于2的职位
select job, count(*) from tb_emp where entrydate <= ‘xxx’ group by job having count(*) >=2;
排序查询
select 字段列表 from 表名 [where 条件] [group by 分组字段] order by 字段1 排序方式,字段2 排序方式…;
asc升序
desc降序
分页查询
select 字段列表 from 表名 limit 起始索引,查询记录条数;
从启始索引0开始查询员工数据,每页展示5条记录
select * from tb_emp limit 0,5;
从启始第1页开始查询员工数据,每页展示5条记录
select * from tb_emp limit 0,5;
从启始第2页开始查询员工数据,每页展示5条记录
select * from tb_emp limit 5,5;
从启始第3页开始查询员工数据,每页展示5条记录
select * from tb_emp limit 10,5;
启始索引(页码-1)*每页展示记录数
案例
按需求完成员工管理的条件分页查询,根据输入条件,查询第一页数据,每页展示10条记录
select * from tb_emp where name like ‘张%’ and gender = 1 and entrydate between ‘xxx’ and ‘xxx’ order by update desc limit 0,10 ;
- if(条件,true取值,false取值)
select if(gender=1,’男性员工’,’女性员工’) 性别 ,count(*) from tb_emp group by gender;
- case 表达式 when 值1 then结果1 when 值2 then 结果2 else … end.
select (case job when 1 then ‘班主任’ when 2 then ‘讲师’ else ‘未分配职位’ end) 职位,count(*) from tb_emp froup by job;
多表设计
一对多
多的一方关联一的一方
问题分析:两张表,在数据库层面并未建立关联,所以是无法保证数据的一致性和完整性的
外键语法
创建表时指定(所有字段罗列完后在最后添加)
1 | create table 表名( |
建完表后,添加外键
1 | alter table 表名 add constraint 外键名称 foreign key (外键字段名) references 主表(字段名); |
这种外键叫物理外键
概念:使用foreign key 定义外键关联另外一张表
缺点:
- 影响CRUD
- 仅用于单节点数据库,不适用分别是,集群场景
- 容易引发数据库的死锁问题,消耗性能
逻辑外键
概念:在业务层逻辑中,解决外键关联
通过逻辑外键,就可以很方便的解决上述问题
一对一
关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他字段放在另一张表中,以提升操作效率
如果基本信息查询特别高,查询身份信息很低,可以拆分为两张表
实现:在任意一方加入外键,关联另一方的主键,并且设置外键为唯一的(UNIQUE)
*多对多
实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
多表查询
内连接查询
查询交集部分
表起别名在表后空格直接加名字
隐式内连接
1 | select 字段列表 from 表1,表2 where 条件...; |
显式内连接
1 | select 字段列表 from 表1 [inner] join 表2 on 连接条件...; |
外连接
左外连接(完全包含左表数据)
1 | select 字段列表 from 表1 left [outer] join 表2 on 连接条件...; |
右外连接(完全包含右表数据)
1 | select 字段列表 from 表1 right [outer] join 表2 on 连接条件...; |
子查询
SQL语句中嵌套select语句,成为嵌套查询,又称子查询
形式
1 | select * from t1 where column1=(select column1 from t2 ...); |
子查询外部的语句可以式insert/update/delete/select
分类
- 标量子查询:子查询返回的结果为单个值
常用的操作符:= 、<>、 >、 >=、 <、 <=
1 | select * from tb_emp where dept_id = (select id from tb_dept where name = '教研部'); |
- 列子查询:子查询返回的结果为一列
常用操作符:in 、not in
1 | select * from tb_emp where dept_id in (select id from tb_dept where name = '教研部' or name = '咨询部'); |
- 行子查询返回的结果为一行
常用操作符:=、<>、in 、not in
1 | select * from tb_emp where (entrydate,job)=(select entrydate,job from tb_emp where name='韦一笑'); |
- 表子查询:子查询返回的结果为多行多列
常用操作符:in
1 | select e.*,d.name from (select * from tb_emp where entrydate > '2006-01-01') e, tb_dept d where e.dept_id = d.id; |
事务
事务是一组操作的集合,它是一个不可分割的工作单位。事物会吧所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成果,要么同时失效
注意事项:默认mysql的事物是自动提交的,也就是说,当执行一条dml语句,mysql会立即隐式提交事务
事务控制
- 开启事务:start transaction;/begin;
- 提交事物: commit;
- 回滚事物: rollback;
先开启事务,然后执行多条sql如果成功就使用commit,如果失败则使用rollback
四大特性
- 原子性:最小单元,要么全部成功,要么全部失败
- 一致性:事物完成时,必须使所有数据都保持一致状态
- 隔离性:数据库系统提供的隔离机制,保证事物不受外部兵法操作影响的独立环境下运行
- 持久性:事物一旦提交或回滚,它对数据库中数据的改变就是永久的
索引
index时帮助数据库高效获取数据的数据结构
优点:
- 提高数据库查询的效率,降低数据库的IO成本
- 通过索引列对数据进行排序,降低数据排序的成本,降低CPU消耗
缺点
- 索引会占用存储空间
- 索引大大提高了查询效率,同时也降低了insert、updata、delete的效率
B+tree(多路平衡搜索树)
语法
- 创建索引:
1 | create [unique] index 索引名 on 表名(字段名); |
- 查看索引
1 | show index from 表名; |
- 删除索引
1 | drop index 索引名 on 表名; |
MyBatis
快速入门
使用Mybatis查询所有用户数据
1、准备工作(创建springboot工程、数据包user、实体类User)
2、引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)
3、编写SQL语句(注解/XML)
四要素
1 | #驱动类名称 |
1 |
|
JDBC
jdbc就是使用java语言操作关系型数据库的一套API
是一套操作关系型数据库的规范,即接口
各个数据库厂商起实现这套接口,体空数据库的java包
我们可以使用这套接口编程,真正执行的代码是驱动jar包中的实现类
数据库连接池
数据库连接池是个容器,复制分配、管理数据库连接(Connection)
它运行应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接泄漏
标准接口
功能:获取链接:Connection getConnection() throws SQLException;
常见产品
C3P0、DBCP、DRuid(阿里巴巴开源)、Hikari(springboot默认)
切换链接池
- 引入依赖
1 | <dependency> |
- 配置链接信息
1 | #驱动类名称 |
lombok
lombok是一个实用的java类库,能通过注解的形式自动生成构造器,getter/setter、equals、hashcode、toString等方法,并可以自动化生产日志变了,简化java开发,提高效率
注解 | 作用 |
---|---|
@Getter/@Setter | 为所有的属性提供get/set方法 |
@ToString | 会给类自动生成一阅读的toString方法 |
@EqualsAndHashCode | 根据类所拥有的非静态字段自动重写equals方法和hashCOde方法 |
@Data | 提供了更综合的生成代码功能(@Getter+@Setter+@ToString+@EqualsAndHashCode) |
@NoArgsConstructor | 为实体类生成无参的构造器方法 |
@AllArgsConstructor | 为实体类生成处理static修饰的字段之外代有各参数的构造器方法 |
1 | <dependency> |
lombok会在编译时,自动生成对于的java代码。我们使用lombok时,还需要安装一个lombok的插件(idea自带)
基础操作
删除
1 |
|
日志输入
可以在application.properties中,打开mybatis的日志,并输出到控制台
1 | mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl |
预编译sql的优势
性能更高
更安全(防止sql注入)
#{id}就会生成预编译sql
${id}会直接将参数拼接在后面,存在sql注入问题
新增
1 |
主键返回
在数据添加成功后,需要获取插入数据库数据的主键
需要在添加的方法上添加注解
1 |
会将主键封装到id属性中,代表我们需要拿到主键值
更新
1 |
|
查询
1 |
|
数据封装
实体类书姓名和数据库表查询返回到字段名一致,mybatis会自动封装
如果实体类书姓名和数据库表查询返回的字段名不一致,不能自动封装
- 方案一:给字段起别名,让别名于实体类属性一致
1 |
|
- 方案二,通过@Results,@Result注解手动映射封装
1 |
|
- 方案三:开启mybatis的驼峰命名自动映射开关 a_column —>aColumn
1 | mybatis.configuration.map-underscore-to-camel-case=true |
条件查询
1 |
|
XML映射文件
规范
- XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
- XML映射文件的namespace属性为Mapper接口全限定名一致
- XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致
在resources下创建包级目录用‘/’
1 |
|
动态SQL
:条件都不成立去除where,而且会自动去除开头多余的and和or
1 | <where> |
案例
动态更新
alt+end,mybtisx插件自动生成sql
,会自动去除后面多余的逗号
forech
遍历元素
1 | delete from emp where id in |
Collection:遍历的集合
Item:遍历出来的元素
separator:分隔符
open:遍历开始前拼接的SQl片段
close:遍历结束后凭借的SQL片段
sql,include
定义可重复使用的SQL片段 通过属性refid,指定包含的SQL片段
1 | <sql id="xx"> |
开发规范-Restful
- REST 表述性状态转换,他是一种软件架构风格
1 | http://localhost:8080/users/1 GET:查询id为1到用户 |
- url定位资源
- HTTP动词描述操作
REST是风格,是约定方式,约定不是规定,可以打破
描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非耽搁资源如:users、emps、books…
统一响应结果
code:响应码
msg:响应信息
data:返回数据
开发流程
- 查看页面原型明确需求
- 阅读接口文档
- 思路分析
- 接口开发
- 接口测试
- 前后端链调
- 开发流程
明确需求 接口文档 思路分析 接口开发
- 接口调试
postman测试 前后端联调
- 日志小技巧
@Slf4j
1 | log.info() |
pageHelper插件
1 | // 员工信息查询 |
1 |
|
1 | <!--PageHelper分页插件--> |
springboot文件上传,默认单个文件最大允许为1M,如果需要上传大文杰,可以进行如下配置
1 | 配置耽搁文件最大上传大小 |
函数名 | 功能 |
---|---|
String getOriginalFilename(); | 获取原始文件名 |
void transferTo(File dest); | 将接收的文件转存到磁盘文件中 |
Long getSize() | 获取文件的大小,单位,字节 |
Byte[] getBytes(); | 获取文件内容的字节数组 |
InputStream getInputStream(); | 接收到的文件内容的输入流 |
yml
- 大小写敏感
- 数值前边必须有空格作为分隔符
- 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能使用空格
- 缩进的空格数目不重要,只要相同层级的元素左侧对其即可
- #表示注释,从这个自负一致到行尾,都会被解析器忽略
- 对象/Map集合
1 | user: |
- 数组/LIst/Set集合
1 | hobby: |
配置文件遍历注入
@Value(“${key}”)
1 | @Data //提供get set方法 |
解决报错
1 | <dependency> |
数据校验
会话
会话:用户打开浏览器,访问web服务器的资源,会话建立,知道一方断开连接,会话结束,在一次会话中可以包括多次请求和响应
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话多次请求间共享数据
会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
- 令牌技术
Cookie
优点:HTTP协议中支持的技术
缺点:
- 移动端APP无法使用Cookie
- 不安全,用户可以自己禁用Cookie
- Cookie不能跨域
1 | //设置Cookie |
跨域
跨域区分三个维度:协议、ip/域名、端口号
任何一个不一样,都会跨域
Session
优点:存储在服务端,安全
缺点:
- 服务器集群环境下无法直接使用Session
- Cookie的缺点
1 |
|
令牌技术
优势:
- 支持PC端,移动端
- 解决集群环境下的认证问题
- 减轻服务器存储压力
缺点:需要自己实现
JWT (Json Web Token)
定义了一种简洁的,自包含的格式,用于通信双方一json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的
组成
- 第一部分:Header(头),记录令牌类型、签名算法等
- 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等
- 第三部分:Signature(签名),防止Token被篡改、确保安全性、将header、payload、并加入指定密钥,通过指定签名算法计算而来
Base64:是一种基于64个可打印字符(A-Z,a-z,0-9+/)来表示二进制数据等编码方式,=补位符号
引入JWT
1 | <dependency> |
JWT校验时使用的签名密钥,必须和生成JWT令牌时使用的密钥时配套的
如果JWT令牌解析校验时报错说明被篡改或过期
过滤器Filter
概念:Filter过滤器时javaWeb三大组件(Servlet、Filter、Listenser)之一
过滤器可以把对资源的请求拦截下来,从二实现一些特殊的功能
过滤器一般完成一些通用的操作,比如登陆校验,统一编码处理,敏感字符处理
快速入门
- 定义Filter:定义一个类,实现Filter接口,并重写其所有方法
- 配置Filter:Filter类上加@WebFilter(urlPatterns = “/*”)注解,配置拦截资源路径。引导类上加@ServletComponentScan开启Servlet组件支持
1 |
|
放行后访问对呀资源,资源访问完成后,还会回到Filter中嘛? 会
如果回到Filter中,时重新执行还是执行放行后到逻辑呢?执行放行后逻辑
拦截路径 | urlPatterns值 |
---|---|
拦截具体 | /login |
拦截目录 | /emps/* |
拦截所有 | /* |
一个web应用中,可以配置多个过滤器,这个过滤器就形成了一个过滤器链
顺序:注解配置的Filter,优先级时按照过滤器类名(字符串)的自然排序
快速捕获异常command+option+t
拦截器interceptor
快速入门
概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制方法的执行
作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码
定义拦截器 实现HandlerIterceptor接口,并重写所有方法
注册拦截器
1 |
|
addPathPatterns**(“/**”)需要拦截
excludePathPatterns**(“/login”)不需要拦截
拦截路径 | 含义 |
---|---|
/* | 一级路径 |
/** | 任意级路径 |
/depts/* | /depts下的一级路径 |
/depts/** | /depts下的任意级路径 |
Fillter于Interceptor
接口规范不同:过滤器需要实现Filter接口,二拦截器需要实现HandlerInterceptor接口
拦截范围不同:过滤器Filter会拦截所有的资源,而Intercetor只会拦截Spring环境中的资源
异常处理
方法一:在controller的方法中进行try…catch
方案二:全局异常处理器 @TestControllerAdvice
1 |
|
事物处理&AOP
事物是一组操作的集合,它是一个不可分割的工作单位,这些操作,要么同时成功,要么同时失败
操作:
- 开启事务(一组操作开始前,开始事物):start transaction/begin;
- 提交事物(这组操作全部成功后,提交事物):commit;
- 回滚事物(中间任何一个操作出现异常,回滚事物):rollback;
@Transactional
位置:业务层的方法上、类上、接口上
作用:将当前方法交给spring进行事物管理,方法执行前,开启事务;成功执行完毕,提交事物,出现异常,回滚事物
1 | #spring事务管理日志 |
事物进阶
rollbackFor
默认情况下,只有出现RuntimeExcception才回滚异常,rollbackFor属性用于控制出现何种异常类型,回滚事物
propagation
事物传播行为:指的就是当一个事物方法被另一个事物方法调用时,这个事物方法一个如何进行事物控制
1 |
|
属性值 | 含义 |
---|---|
REQUIRED | 默认值,需要事物,有则加入,无则创建新事物 |
REQUIRES_NEW | 需要新事物,无论有无,总是创建新事物 |
SUPPORTS | 支持事物有则加入,无则在无事物状态中运行 |
NOT_SUPPORTED | 不支持事物,在无事物状态下运行,如果当前存在已有事物,则挂起当前事物 |
MANDATORY | 必须有事物,否则抛异常 |
NEVER | 必须每事物,否则抛异常 |
AOP
快速入门:面向切面编程,其实就是面向特定方法编程
动态代理是面向切面编程最主流的实现,二springAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过代理机制,对特定的方法进行编程
导入aop依赖
1 |
|
AOP核心概念
连接点:JoinPoint,可以被AOP控制的方法(暗含执行时的相关信息)
通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
切入点:PointCut,匹配连接点的条件,通知仅在切入点方法执行时被应用
切面:Aspect,描述通知于切入点点对应关系(通知+切入点)
目标对象:Target,通知所有应用对象
通知类型
@Around环绕通知,次注解标注的通知方法在目标方法前,后都被执行
@Before前置通知,此注解的通知方法在目标方法前执行
@After 后置通知,此注解标注的通知方法后被执行,无论是否异常都会被执行
@AfterReturing 返回通知,次注解标注的通知方法在目标方法后被执行,有异常不会被执行
@AfterThrowing:异常后通知,此注解的通知方法发生异常后执行
注意事项
- @Around环绕通知需要自己调用ProceedingJoinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标执行方法
- @Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值
切入点表达式
@PointCut该注解的作用时将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可
1 |
|
通知的执行顺序
当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行
和类名字母排序有关
1.不同切面类中,默认按照切面类的类名字母排序;
目标方法前的通知方法:字母排名靠前的先执行
目标方法后的通知方法:字母排名靠前的先执行
2.@Order(num)设置执行顺序
目标方法前的通知:数字小的先执行
目标方法后的通知:数字小的后执行
切入点表达式
切入点表达式:描述切入点方法的一种表达式
作用:主要用来决定项目中哪些方法需要加入通知
常见形式:
- execution(…)根据方法的签名来匹配
1 | execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?) |
其中带?的表示可以省略的部分
访问修饰符:可省略
包名.类名:可省略(不建议省略)
Throws 异常:可省略
- 可以使哟童年通配符
*:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
1 | execution(* com.*.service.*.update*(*)) |
..:多个连续的任意符号,可以通配任意层级的包、或任意类型、任意个数的参数
1 | execution(* com.itheima..DeptService.*(..)) |
|| 或
书写建议:
所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配
描述切入点方法通常给予接口描述,而不时直接描述实现类,增强拓展性
在满足业务需要的前提下,尽量缩小切入点的匹配范围。
- @annotation(…)根据注解匹配
第一步新建一个自定义注解Mylog
加两个原注解描述
@Retention(RetentionPoliycy.RUNTIME)//运行时有效
@Target(ElementType.METHOD)//作用目标(方法)
第二部在业务方法上加@Mylog注解
然后在切入点表达式
@Pointcut(“@annotation(com.itheima.aop.Mylog)”)去引入这个注解
连接点
在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息、方法名、方法参数等
对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint
对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型
获取目标对象的类名 obj.getTarget().getClass().getName()
获取目标方法的方法名obj.joinPoint.getSignature().getName();
获取目标方法运行时传入的参数obj.getArgs()
放行目标方法执行obj.proceed()
获取目标方法运行的返回值obj就是上面proceed的返回值
配置优先级
Maven分模块开发
分模块开发需要先正对模块功能进行设计,再进行编码,不会先将工程开发完毕,然后进行拆分
创建maven模块tlias-pojo,存放实体类
创建maven模块tlias-utils,存放工具类
1.创建一个maven模块,最外面的包名保持一致,吧pojo全部类复制到爆虾
2.引入xml
继承
- 创建maven模块tlias-parent,该工程为父工程,设置打包方式为pom:
pom
一个工程只能继承一个父工程,单支持多重继承
jar:普通模块打包,springboot项目基本都是jar包内嵌tomaacat运行
war:普通web程序打包,需要部署在外部的tomacat服务器中运行
pom:父工程或聚合工程,该模块不屑代码,进行依赖管理
在子工程中,配置了继承关系之后,坐标中groupId是可以省略的,业务会自动继承父工程的
relativePath指定父工程的pom文件的相对位置(如果不指定,将从本地仓库/远程仓库查找该工程)
若父子工程都配置了同一个依赖的不同版本,以子工程的为标准
版本锁定
在父工程使用此标签,在子工程引入时就可以不用指定版本
聚合
将多个模块组装成一个整体,同时进行项目构建
聚合工程:一个不具有任何业务功能的“空”工程(有切仅于一个pom文件)
作用:快速构建项目
maven中可以通过
在父工程中