HTTP协议
Hyper Text Transfer Protocol
- 基于TCP
- 基于请求-响应模型
- 无状态的协议,不带之前的内容
会话
请求协议
请求行 请求方式,资源路径,协议
请求头
请求体 和请求头有一个空行
响应协议
响应行 协议,状态码,描述
- 1xx 响应中
- 2xx 成功
- 3xx 重定向
- 4xx 客户端错误
- 5xx 服务器错误
响应头
响应体
协议解析
Apache Tomcat
webapps目录
conf/server.xml 改端口
默认端口80
logging.properties 改UTF-8
DispatcherServlet 前端控制器
HttpServletRequest 请求对象
HttpServletResponse 响应对象
请求响应
请求
- 简单参数
- 实体参数
- 数组 String[]/@RequstParam List
- @RequestParam(defaultValue=“1”)如果没给参数默认值
- 日期 DateTimeFormat
- Json @RequestBody
- @RequestBody Dept dept
- 路径 {id} @PathVariable
- @PathVariable Integer id
响应
@ResponseBody
@RestController=@ResponseBody+@Controller
统一响应结果
public class Result{
private Integer code;
private String msg;
private Object data;
……
}
解析xml文件 dom4j
@RequestMapping(value="/depts"method=RequestMethod.GET)
->
@GetMapping("")
...
分层解耦
Controller
Service
Dao
- 内聚
- 耦合
解耦 容器
- 控制反转 impl->容器
- Inversion Of Control
- 依赖注入 容器->service
- Dependency Injection
- Bean IOC容器中创建、管理的对象
@Component 控制反转,交给容器
@Autowired 依赖注入
IOC
- @Component
- @Controller
- @Service
- @Repository
组件扫描
@ComponentScan({“…”,“…”})
指定扫描范围,默认本包和子包
包含在@SpringBootApplication
DI
Bean注入报错
- @Primary
- @Qualifer
- @Resource
MySQL
DataBase Management System
Structured Query Language SQL
- DDL 数据定义语言
- DML 数据操作语言
- DQL 数据查询语言
- DCL 数据控制语言
DDL
show databases;
select database();
create database ;
drop database ;
use ;
--也可以用schema
create table 表名(
字段 字段类型 [约束] [comment 字段注释]
)[comment 表注释]
- not null
- unique
- 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
update
delete
DQL
SELECT
基本查询
select
from
where
group by
having
order by
limit
条件查询
>=<
between ... and ...
in ...
like _ %
(查询两个字 like '__')
is null
and &&
or ||
not !
分组查询
-- 聚合函数
count
-- count (字段)
-- count (常量)
-- count (*)
max
min
avg
sum
group by 字段
having 分组后的条件(where在分组之前过滤)
- 分组之后,查询的一般为聚合函数和分组字段
- where->聚合函数->having
排序查询
order by
ASC 升序 DESC 降序
多个排序字段用 ,
分页查询
limit 起始索引,查询记录数
第一页的索引是0,相应的,第二页索引是查询记录数
如果查询第一页可以不写起始索引
案例
select if(gender = 1,'男性员工','女性员工') 性别,count(*) from emp group by gende;
-- true第二个值,false第三个值,后面别名是筛选出的列名
-- 就是把拿到的gender处理
select
(case job when 1 then '班主任' ... else '未分配' end) 职位
form emp group by job;
多表设计
一对多
外键约束 一致性完整性
[constraint] [外键名称] foreign key (外键字段名) references 主表(字段名)
alter table 表名 add constraint 外键名称 foreign key (外键字段名) references 主表(字段名)
-- 外键约束添加在子表,也就是一对多的多
- 物理外键
- 影响增删改效率
- 仅用于单节点数据库不适用分布式、集群
- 容易引发死锁
- 逻辑外键
一对一
任意一方加入外键,关联另外一方主键,将外键设为唯一unique(一对一,一个值对应一个值,一个值不会对应两个值)
就是加上外键约束,然后将外键那个字段加上unique
多对多
第三张中间表,至少两个外键,关联两方主键
多表查询
笛卡尔积
比如A表和B表查询,select * frome A,B,会返回所有组合情况,这就是笛卡尔积,但实际有很多是无效的所以要加条件。
X | 1 | 1 | AA | ||||
---|---|---|---|---|---|---|---|
X | 1 | 2 | BB | ||||
1 | AA | X | 1 | 3 | CC | ||
X | 1 | 2 | BB | X | 1 | 4 | DD |
Y | 2 | 3 | CC | Y | 2 | 1 | AA |
4 | DD | Y | 2 | 2 | BB | ||
Y | 2 | 3 | CC | ||||
Y | 2 | 4 | DD |
加上条件
X | 1 | 1 | AA | ||||
---|---|---|---|---|---|---|---|
1 | AA | ||||||
X | 1 | 2 | BB | ||||
Y | 2 | 3 | CC | ||||
4 | DD | Y | 2 | 2 | BB | ||
- 连接查询
- 内连接
- 隐式内连接
- from 表1,表2 where
- 显式内连接
- from 表1 [inner] join 表2 on
- 隐式内连接
- 外连接
- 左外连接
- left [outer] join
- 右外连接
- right [outer] join
- 左外连接
- 内连接
- 子查询
- 外部语句可以是insert/update/delete/select
- 标量子查询 >=<
- 列子查询 一列多行,比如id列 in, not in
- 行子查询 一行多列,比如一条相同记录 =,<>,in,not in
- 组合值 (id,name)=(1,zhangsan)
- 表子查询 比如对一个表先筛选再和另一个表对应 in或者临时表
事务
一组操作的集合,把操作作为整体像系统提交,所以要么同时成功要么同时失败。
MYSQL中每条DML语句隐式提交事务,所以可能有问题。
start transaction;/begin;
commit; -- commit之前别的窗口是不变的,因为相当于不同的事务,事务隔离
rollback;
特性
- 原子性 要么全部成功,要么全部失败
- 一致性 数据保持一致
- 隔离性 保证事务不受外部并发影响
- 持久性 事务一旦提交或回滚,对数据改变是永久的
ACID
索引
优化
- 索引
- SQL优化
- 分库分表
create index 索引名 on 表名(字段名);
全表扫描->索引
索引是帮助高效获取数据的数据结构
默认B+Tree结构,多路平衡搜索树
create [unique] index 索引名 on 表名(字段名,...);
-- 主键默认主键索引
-- unique唯一约束其实就是添加了索引
show index form 表名;
drop index 索引名 on 表名;
Mybatis
持久层(Dao)框架,简化JDBC开发
Dao层@Repository->@Mapper
@SpringBootTest整合单元测试注解
配置sql提示
右键show context actions->inject language->mysql
JDBC
Java DataBase Connectivity
操作关系型数据库的一套API,一套接口
注册驱动
获取连接对象
创建Statement对象执行SQL返回结果
封装结果数据
释放资源
数据库连接池
数据库连接池
标准接口:DataSource
Connection getConnection() throws SQLException;
Hikari追光者 Druid德鲁伊 DBCP C3P0
lombok
@Getter/@Setter
@ToString
@EqualsAndHashCode
@Data = @Getter/@Setter + @ToString + EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
操作
删除
#{} 占位符
删除 预编译SQL
预编译SQL
- 性能更高
- 更安全,防止SQL注入
{SQL语法解析检查->优化SQL->编译SQL}(缓存)->执行SQL
预编译就不用每个不同变量都经过缓存了->性能更高
- #{…} 参数传递
- ${…} 拼接SQL
新增
主键返回
@Options(keyProperty = "id", useGeneratedKeys = true)
更新
update 表名 set …
实体类
查询
数据封装:
- 实体类属性名和数据库查询返回字段名不一致不能自动封装
解决方案:
- 起别名和实体类属性一致
- 改SQL语句,加上起别名
- 注解
- @Results({@Result(column=“”,property=“”),…})
- 开启驼峰命名映射
- mybatis.configuration.map-underscore-to-camel-case=true
条件查询模糊匹配
like '%zzz%'
->
like '%${name}%' -- 因为%%不能用#{}
-- 效率低,SQL注入
—>
like concat('%',#{name},'%')
-- concat拼接
1.x版本/单独使用mybatis
需要@Param注解
XML
规范:
- 同包同名
- namespace和mapper接口全限定名一致,也就是路径名 (namespace)
- id与方法名一致(id),返回类型一致(resultType)
动态SQL
<if>拼接SQL,使用test属性条件判断,例如<if test="name!=null">
<where>动态,会去掉多余的and或or
<set>去掉多余逗号,update中使用
where子句
->
<where>
<set>
<if>
</if>
</set>
</where>
<forEach>
-- collection:遍历集合
-- item:元素
-- separator:分隔符
-- open:遍历开始SQL片段
-- close:遍历结束SQL片段
<forEach collection="ids",item="id",separator=",",open="(",close=")">
#{id}
</forEach>
<sql><include>
抽取可重用SQL片段和引用
<sql id="selectAll">
...
</sql>
<include refid="selectAll"/>
开发规范
Restful
REST REpresentational State Transfer 表示性状态转换,软件架构风格
REST风格
- URL定位资源
- HTTP动词描述操作
统一响应结果Result
日志记录
private static Logger log = LoggerFactory.getLogger(DeptController.class);
log.info("这个接口查询全部部门数据");
->
@Slf4j
log.info("...");
controller=>service=>impl=>mapper=>xml/注解
查询结果封装Bean
//分页查询
controller层
->传参,@RequestParam,返回封装Bean
service层
->调用实现方法获得返回值,new Bean对象
mapper层
->sql
//分页插件PageHelper
引入依赖
mapper层不用limit
service层
->
设置分页参数PageHelper.startPage(page,pageSize)
执行查询List<Emp> empList = empMapper.list();Page<Emp> p = (Page<Emp>) empList
封装PageBean对象PageBean pageBean = new PageBean(p.getTotal(),p.getResult())
文件上传三要素
MultipartFile接收
文件上传–阿里云OSS
配置文件
配置优先级
properties > yml > yaml
除了配置文件另外两种,命令行 > Java系统属性 > 配置文件
Java系统属性
-Dserver.port=9000
命令行参数
–server.port=9000
打包后指定
java -Dserver.port=9000 -jar … --server.port=10010
参数配置化
配置在appication.properties
通过@Value(“${aliyun.oss.endpoint}”)注解用于配置属性注入
yml配置文件
- 大小写敏感
- 数值前要有空格作为分隔符
- 缩进层级关系,不能tab(idea会自动转化)
- 相同层级的元素左侧对齐就可以
- #注释
数据格式
# 定义对象/Map集合
user:
name: Tom
age: 20
address: beijing
# 数组/List/Set集合
hobby:
- java
- C
- game
- sport
# 数据库
spring:
datasourse:
driver-class-name:
url:
username:
password:
# Mybatis配置
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
@ConfigurationProperties
@Value->注入类,批量的将外部属性配置注入到bean对象的属性中
@Data
@Component
@ConfigurationProperties(prefix="aliyun.oss")
public class AliOSSProperties{
//对应配置项
}
依赖spring-boot-configuration-processor可选
登录校验
统一拦截->Filter过滤器/Interceptor拦截器
登录标记->会话技术
会话技术
会话
会话跟踪
会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
- 令牌技术
Cookie
- Cookie
- Set-Cookie
//设置Cookie
public Result cookie1 (HttpServletResponse response){
response.addCookie(new Cookie("name","value"));
return Result.success();
}
//获得Cookie
public Result cookie2 (HttpServletRequest request){
Cookie[] cookies= request.getCookies();
for(Cookie cookie:cookies){
if(cookie.getName().equals("name")){
System.out.printlan("name"+cookie.getValue());
}
return Result.success();
}
}
跨域区:
协议,IP/域名,端口
JWT令牌
JSON Web Token
简洁的、自包含的格式
组成:
- Header,记录令牌类型,签名算法等 base64
- Payload,有效载荷
- Signature,签名,防止token被篡改、确保安全性
//生成
Jwts.buidler()
.signWith(SignatureAlogorithm.HS256,"test")
.setClaims(test)
.setExpiration(new Date(System.currentTimeMillis()+1000))
.compact();
//解析
Jwts.parser()
.setSigningKey("test")
.parseClaimsJws("")
.getBody();
//登录后下发
Map<String,Object> claims = new HashMap<>();
claims.put("id",e.getId());
String jwt = JwtUtils.generateJwt(claims);
过滤器Filter
Servlet Filter Listener JavaWeb三大组件
@WebFilter(urlPatterns="/*")//Filter类
public void init(FilterConfig filterConfig) throws ServletException{
Filter.super.init(filterConfig);
}
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain){
System.out.println("");
//放行
chain.doFilter(request,response);
}
public void destroy(){
Filter.super.destroy();
}
@ServletComponentScan //启动类
执行流程
放行前
放行
放行后(会回到Filter)
拦截路径
/login
/login/*
/*
过滤器链
排序按类名排序
@Override
@WebFilter(urlPatterns="/*")
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
//拿URL
String url = req.getRequestURL().toString();
log.info("",url);
//登录就放行
if(url.contains("login")){
log.info("");
chain.doFilter(request.response);
return;
}
//拿jwt
String jwt = req.getHeader("token");
//没登陆
if(!StrignUtils.hasLength(jwt)){
log.info("");
Result error = Result.error("NOT_LOGIN");
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//解析
try{
JwtUtils.parseJWT(jwt);
}catch(Exception e){
log.info("");
Result error = Result.error("NOT_LOGIN");
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//解析通过
lgo.info("放行");
chain.doFilter(request,response);
}
拦截器Interceptor
HandlerInterceptor
ctrl+O
@Component
public class LoginCheckInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest req,HttpServletResponse resp,Object handler)throws Exception{
System.out.println("目标资源方法执行前");
return true;
}
@Override
public void postHandle(HttpServletRequest req,HttpServletResponse resp,Object handler,ModelAndView modelAndView){
System.out.println("目标资源方法执行后");
}
@Override
public void afterCompletion(HttpServletRequest req,HttpServletResponse resp,Object handler,Exception ex){
System.out.println("视图渲染完毕后");
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer{
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
publci void addInterceptores(InterceptorRegistry registry){
registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
}
}
拦截路径
registry.addInterceptor(loginCheckInterceptor).addPathPatterns(“/**”).excludePathPatterns(“/login”);
- /*
- /**
- /depts/*
- /depts/**
执行流程
Filter->DispatchServlet(Tomcat识别)->Interceptor->Controller层
区别:
- 接口规范不同:Filter,HandlerInterceptor
- 拦截范围不同:Filter所有,Interceptor只拦截Spring环境
放行通过return true,不放行false
异常处理
全局异常处理器
现在前端错误信息是JSON没法解析,异常处理后前端可以收到
@RestControllerAdvice=@ControllerAdvie+@Resposnebodys
@RestControllerAdvice
public class GlobalExceptionHandler{
@ExceptionHandler(Exception.class)//捕获所有异常
public Result ex(Exception ex){
ex.printStackTrace();
return Result.error("111");
}
}
事务管理
@Transactional
方法 类 接口
日志
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
@rollbackFor
默认异常回滚只回滚RuntimeException
rollbackFor控制出席那何种异常属性回滚事务
@Transactional(rollbackFor=Exception.clss)
@propagation=Propagation.?
事务传播行为:当一个事务方法被另一个事务方法调用,事务方法如何进行事务控制
- REQUIRED 有则加入,无创建新事务
- REQUIRED_NEW 都创建新事务
- SUPPORTS
- NOT_SUPPORTED
- MANDATORY
- NEVER
- …
finally{}不论是否异常都记录日志
- REQUIRED 有则加入,无创建新事务 大部分可以
- REQUIRED_NEW 都创建新事务 当不希望事务之间相互影响时可以使用该传播行为
AOP
Aspect Oritented Programming
面向特定方法编程
动态代理
记录日志、权限控制、事务管理、…
- 代码无侵入
- 少重复
- 提高开发效率
- 维护方便
入门程序
PrceedingJoinPoint joinPoint
@Component
@Aspect//aop类
public class TimeAspect{
@Around("execution(* com.itheima.service.*.*(..))")//用在哪个类,第一个*是返回类型,表示返回任意类型
public Object recordTime(ProceedingJoinPoint joinPoint){
long begin = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
log.info(joinPoint.getSignature+"{}",end-beegin);
return result;
}
}
核心概念
- 连接点 JoinPoint 可以被AOP控制的方法
- 通知 Advice 共性功能,方法
- 切入点 PointCut 匹配连接点的条件,通知只在切入点方法执行时应用 @Around
- 切面 Aspect 通知和切入点的对应关系(通知+切入点)
- 目标对象 Target 通知所应用的对象
AOP执行流程
生成对应代理对象,过程注入代理对象
CGLIB动态代理
通知类型
- @Around 环绕通知
- @Before 前置通知
- @After 后置通知,也叫最终通知
- @AfterReturning 返回后通知
- @AfterThrowing 异常后通知
声明切入点
@Pointcut("...")
private void pc(){}
@Around("pt()")
通知顺序
- 类名字母
- 目标方法前,靠前先执行
- 目标方法后,靠前后执行
- @Order
- 目标方法前,数字小先执行
- 目标方法后,数字小后执行
切入点表达式@Pointcut(“”)
- execution(…) 根据方法的签名来匹配
- execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
- * 单个任意符号
- … 多个连续的任意符号
- @annotation(…) 根据注解匹配
- 自定义注解
- public @interface MyLog
- @Retention(RententionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- @annotation(“com.itheima.aop.MyLog”)
- 自定义注解
连接点
JoinPoint
- @Around ProceedingJoinPoint
- 其他四种通知 JoinPoint
JoinPoint 是 ProceedingJoinPoint 父类
案例 记录日志
//自定义注解
@Retention(RententionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log{
}
//切面类
@Slf4j
@Component
@Aspect
public class LogAspect{
@Arount("@annotation(...)")
public Object recordLog(ProceedingJoinPoint joinPoint){
String jwt = request.getHeader("token");
Claims claims = JwtUtils.parseJWT(jwt);
Integer operateUser = (Integer) claims.get("id");
LocalDateTime operateTime = LocalDateTime.now();
Stirng className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignatur().getName();
Object[] args = joinPoint.getArgs();
String methodParams = Arrays.toString(args);
long begin = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
String returnValue = JSONObject.toJSONString(result);
Long costTime = end - begin;
OperateLog operateLog = new OperateLog(...);
operateLogMapper.insert(operateLog);
log.info("{}",operateLog);
return result;
}
}
bean管理
先自动注入
bean系统创建是和类一样首字母小写
- 名称获取
- DeptController bean = (DeptController) applicationContext.getBean(“deptController”);
- 类型获取
- DeptController bean = applicationContext.getBean(DeptController.class)
- 名称类型获取
- DeptController bean = applicationContext.getBean(“deptController”,DeptController.class)
bean作用域
- singleton 单例
- prototype 每次使用创建新的
- request 每个请求
- session 每个会话
- application 每个应用
@Lazy 使用的时候才初始化创建Bean,默认启动就创建
@Scope(“…”)
第三方bean
@Bean
@Bean //将当前方法的返回值对象交给IOC容器管理,成为IOC容器bean
public SAXReader(){
return new SAXReader();
}
//配置类统一管理
@Configuration
public class CommonConfig{
@Bean //将当前方法的返回值对象交给IOC容器管理,成为IOC容器bean
//通过name/value指定,默认方法名
public SAXReader saxReader(){
return new SAXReader();
}
}
//如果要注入依赖,直接在bean定义方法设置形参即可,容器会注入
SpringBoot原理
起步依赖
spring-boot-starter-web集成了web开发的常见依赖
通过依赖传递实现
自动配置
spring容器启动后,一些配置类、bean对象自动存入了IOC容器
自动配置原理
方案一:
引入依赖,然后通过@ComponentScan扫描
方案二:
@Import导入,使用@Import导入的类会被Spring加载到IOC容器中
@Import({…})
-
导入普通类
-
导入配置类
-
导入ImportSelector接口实现类
-
封装@EnableHeaderConfig
-
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(MyImportSelector.class) public @interface EnableHeaderConfig{ }
-
源码跟踪
@SpringBootApplication
->
@SpringBootConfiguration
@ComponentScan
@EnableAutoConfiguration
->
@Import(AutoConfigurationImportSelector.class)
交给IOC容器管理
->
String[] selectImports(…)方法,加载全类名
->
spring.factories旧版本
spring目录下org.springframework.boot.autoconfigure.AutoConfiguration.imports
->
全类名
XXXAutoConfiguration
不是所有都注册到IOC
@ConditionalOnMissingBean 按条件装配
@Conditional 父注解
- @ConditionalOnClass
- @ConditionalOnMissingBean
- @ConditionalOnProperty
- …
案例-自定义starter起步依赖
依赖管理功能
自动配置功能
aliyun-oss-spring-boot-starter
- aliyun-oss-spring-boot-starter.iml
- pom.xml
- com.aliyun.oss
- aliyun-oss-spring-boot-autoconfigure
aliyun-oss-spring-boot-autoconfigure
- AliOSSProperties
- AliOSSUtils
- AliOSSAutoConfiguration
- 依赖
META-INF/spring/xxx.imports
- 全类名
总结
- JavaWeb 过滤器,Cookie,Session
- 解决方案 JWT,阿里云OSS
- SpringMVC 接受请求,响应数据,拦截器,全局异常处理
- Spring framework IOC,DI,AOP,事务管理
- Mybatis
- SpringBoot
SSM框架->SpringBoot
Maven高级
分模块设计与开发
继承与聚合
继承关系
- 父工程,设置打包方式pom
- 子工程继承关系
- 父工程配置依赖
版本锁定
父工程
子工程不用标注version使用
自定义属性
<properties>
<lombok.version>1.18.24</lombok.version>
</properties>
聚合
- 聚合
- 将多个模块组织成整体,同时进行项目的构建
- 聚合工程
- 一个不具有业务功能的工程(父工程)
- 作用
- 快速构建项目,直接在聚合工程上构建即可
…