Hyper Text Transfer Protocol
会话
请求行 请求方式,资源路径,协议
请求头
请求体 和请求头有一个空行
响应行 协议,状态码,描述
响应头
响应体
Apache Tomcat
webapps目录
conf/server.xml 改端口
默认端口80
logging.properties 改UTF-8
DispatcherServlet 前端控制器
HttpServletRequest 请求对象
HttpServletResponse 响应对象
@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
解耦 容器
@Component 控制反转,交给容器
@Autowired 依赖注入
组件扫描
@ComponentScan({“…”,“…”})
指定扫描范围,默认本包和子包
包含在@SpringBootApplication
Bean注入报错
DataBase Management System
Structured Query Language SQL
show databases;
select database();
create database ;
drop database ;
use ;
--也可以用schema
create table 表名(
字段 字段类型 [约束] [comment 字段注释]
)[comment 表注释]
数据类型
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 表名;
insert
update
delete
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在分组之前过滤)
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 | ||
MySQL 练习实践 (w3ccoo.com)
一组操作的集合,把操作作为整体像系统提交,所以要么同时成功要么同时失败。
MYSQL中每条DML语句隐式提交事务,所以可能有问题。
start transaction;/begin;
commit; -- commit之前别的窗口是不变的,因为相当于不同的事务,事务隔离
rollback;
特性
ACID
优化
create index 索引名 on 表名(字段名);
全表扫描->索引
索引是帮助高效获取数据的数据结构
默认B+Tree结构,多路平衡搜索树
create [unique] index 索引名 on 表名(字段名,...);
-- 主键默认主键索引
-- unique唯一约束其实就是添加了索引
show index form 表名;
drop index 索引名 on 表名;
持久层(Dao)框架,简化JDBC开发
Dao层@Repository->@Mapper
@SpringBootTest整合单元测试注解
配置sql提示
右键show context actions->inject language->mysql
Java DataBase Connectivity
操作关系型数据库的一套API,一套接口
注册驱动
获取连接对象
创建Statement对象执行SQL返回结果
封装结果数据
释放资源
数据库连接池
标准接口:DataSource
Connection getConnection() throws SQLException;
Hikari追光者 Druid德鲁伊 DBCP C3P0
@Getter/@Setter
@ToString
@EqualsAndHashCode
@Data = @Getter/@Setter + @ToString + EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
#{} 占位符
删除 预编译SQL
预编译SQL
{SQL语法解析检查->优化SQL->编译SQL}(缓存)->执行SQL
预编译就不用每个不同变量都经过缓存了->性能更高
主键返回
@Options(keyProperty = "id", useGeneratedKeys = true)
update 表名 set …
实体类
数据封装:
解决方案:
条件查询模糊匹配
like '%zzz%'
->
like '%${name}%' -- 因为%%不能用#{}
-- 效率低,SQL注入
—>
like concat('%',#{name},'%')
-- concat拼接
1.x版本/单独使用mybatis
需要@Param注解
规范:
<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风格
统一响应结果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}”)注解用于配置属性注入
数据格式
# 定义对象/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
@Value->注入类,批量的将外部属性配置注入到bean对象的属性中
@Data
@Component
@ConfigurationProperties(prefix="aliyun.oss")
public class AliOSSProperties{
//对应配置项
}
依赖spring-boot-configuration-processor可选
统一拦截->Filter过滤器/Interceptor拦截器
登录标记->会话技术
会话
会话跟踪
会话跟踪方案:
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/域名,端口
JSON Web 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);
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);
}
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”);
执行流程
Filter->DispatchServlet(Tomcat识别)->Interceptor->Controller层
区别:
放行通过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.?
事务传播行为:当一个事务方法被另一个事务方法调用,事务方法如何进行事务控制
finally{}不论是否异常都记录日志
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;
}
}
核心概念
AOP执行流程
生成对应代理对象,过程注入代理对象
CGLIB动态代理
通知类型
声明切入点
@Pointcut("...")
private void pc(){}
@Around("pt()")
通知顺序
切入点表达式@Pointcut(“”)
连接点
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作用域
@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定义方法设置形参即可,容器会注入
起步依赖
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 父注解
案例-自定义starter起步依赖
依赖管理功能
自动配置功能
aliyun-oss-spring-boot-starter
aliyun-oss-spring-boot-autoconfigure
META-INF/spring/xxx.imports
SSM框架->SpringBoot
继承关系
版本锁定
父工程
子工程不用标注version使用
自定义属性
<properties>
<lombok.version>1.18.24</lombok.version>
</properties>
聚合
…