YApi
是一个高效、易用、功能强大的api管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。
地址:http://yapi.smart-xwork.cn/
Nginx
NIginx是一款轻量级的web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。其特点是占有内存少,并发能力强,在各大型互联网公司都有非常广泛的使用。
官网:http://nginx.org/
conf配置文件可以用来开启自定义窗口。
html用来放入vue 经过 npm run build 生成的dist
双击nginx启动。
maven
安装步骤
依赖配置
依赖传递
- 排除依赖
依赖范围
生命周期
生命周期阶段
- clean:移除上一次构建生成的文件
- compile:编译项目源代码
- test:使用合适的单元测试框架运行测试(junit)
- package:将编译后的文件打包,如:jar、war等
- install:安装项目到本地的仓库
在同一套生命周期中,后面的阶段执行时,前面也会运行。(上述中除了clean属于clean周期,其他全部属于default周期。)
- 删除targer文件包
输入cmd打开命令行窗口,输入mvn clean 即可删除成功
Spring
SpringBootWeb入门
- 创建Spring项目
这些文件可有可无
- 创建请求处理类HelloController,并添加注解
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
System.out.println("hello World~");
return "hello World";
}
}
HTTP协议
http-请求数据格式
请求响应-http响应格式
一、状态码大类
状态码分类 | 说明 |
---|---|
1xx | 响应中——临时状态码,表示请求已经接受,告诉客户端应该继续请求或者如果它已经完成则忽略它 |
2xx | 成功——表示请求已经被成功接收,处理已完成 |
3xx | 重定向——重定向到其它地方:它让客户端再发起一个请求以完成整个处理。 |
4xx | 客户端错误——处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等 |
5xx | 服务器端错误——处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP版本不支持等 |
二、常见的响应状态码
状态码 | 英文描述 | 解释 |
---|---|---|
200 | OK | 客户端请求成功,即处理成功,这是我们最想看到的状态码 |
302 | Found | 指示所请求的资源已移动到由Location 响应头给定的 URL,浏览器会自动重新访问到这个页面 |
304 | Not Modified | 告诉客户端,你请求的资源至上次取得后,服务端并未更改,你直接用你本地缓存吧。隐式重定向 |
400 | Bad Request | 客户端请求有语法错误,不能被服务器所理解 |
403 | Forbidden | 服务器收到请求,但是拒绝提供服务,比如:没有权限访问相关资源 |
404 | Not Found | 请求资源不存在,一般是URL输入有误,或者网站资源被删除了 |
405 | Method Not Allowed | 请求方式有误,比如应该用GET请求方式的资源,用了POST |
428 | Precondition Required | 服务器要求有条件的请求,告诉客户端要想访问该资源,必须携带特定的请求头 |
429 | Too Many Requests | 指示用户在给定时间内发送了太多请求(“限速”),配合 Retry-After(多长时间后可以请求)响应头一起使用 |
431 | Request Header Fields Too Large | 请求头太大,服务器不愿意处理请求,因为它的头部字段太大。请求可以在减少请求头域的大小后重新提交。 |
500 | Internal Server Error | 服务器发生不可预期的错误。服务器出异常了,赶紧看日志去吧 |
503 | Service Unavailable | 服务器尚未准备好处理请求,服务器刚刚启动,还未初始化好 |
状态码大全:https://cloud.tencent.com/developer/chapter/13553
Web服务器-Tomcat
- 服务器
对http协议操作进行封装,简化web程序开发
- Tomcat
一个轻量级的web服务器,支持servlet、jsp等javaee规范。
也被称为称为web容器,servlet容器。
javase:标准版
javame:小型版
javaee:企业版
基本使用
操作
起步依赖
- spring-boot-starter-web:包含了web应用开发所需要的常见依赖。
- spring-boot-stater-test:包含了单元测试所需要的常见依赖。
请求响应
postman
是一款功能强大的网页调试与发送网页http请求的chrome插件。
作用:常用于进行接口测试
- 简单参数
//1. 简单参数
//下面的第一条语句重中之重,不能丢
@RestController
public class RequestController {
//原始方式
@RequestMapping("/simpleParam")
public String simpleParam(HttpServletRequest request){
//获取请求参数
String name = request.getParameter("name");
String ageStr = request.getParameter("age");
int age = Integer.parseInt(ageStr);
System.out.println(name+ ":" + age);
return "OK";
}
//springboot方法
@RequestMapping("/simpleParam")
public String simpleParam(String name, Integer age){
System.out.println(name+ ":" + age);
return "OK";
}
//如果方法形参名称与请求参数名称不匹配,可以使用@RequestParam完成映射。
//@RequestParam中的required属性默认值为true,代表请求参数必须传递,如果不传递将报错。如果该参数是可以选的,可以将required属性设置为false。
@RequestMapping("/simpleParam")
public String simpleParam(@RequestParam(name = "name", required = false) String username, Integer age){
System.out.println(username+ ":" + age);
return "OK";
}
}
+get请求
- post请求
- 简单实体参数
请求参数名与形参对象属性名相同,定义POJO接收即可。
第二个是再定义一个user对象,第一个是直接将对象作为型参进行传参。
- 复杂实体对象
复杂实体对象:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数。
- 数组and集合集合参数
数组:请求参数名与型参数组名称相同且参数为多个,定义数组类型形参即可接收参数。
集合:请求参数名与形参中集合变量名相同,通过@RuqestParam绑定参数关系。
//数组
@RequestMapping("/listParam")
public String listParam(String[] hobby){
System.out.println(Array.toString(hobby));
return "OK";
}
//集合
@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby){
System.out.println(hobby);
return "OK";
}
- 日期参数
使用@DateTimeFormat注解完成日期参数格式转换
@RequestMapping("/dateParam")
public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime){
System.out.println(updateTime);
return "OK";
}
- Json参数
@RequestMapping("/jsonParam")
public String jsonParam(@RequestBody User user){
System.out.println(user);
return "OK";
}
- 路径参数
通过请求URL直接传递参数,使用{……}来标识该路径参数,需要使用@PathVariable获取路径参数。
//一个参数
@RequestMapping("/path/{id}")
public String pathParam(@PathVariable Integer id){
System.out.println(id);
return "OK";
}
//多个参数
@RequestMapping("/path/{id}/{name}")
public String pathParam2(@PathVariable Integer id , @PathVariable String name){
System.out.println(id);
System.out.println(name);
return "OK";
}
响应数据
例子:
public class Result {
private Integer code ;//1 成功 , 0 失败
private String msg; //提示信息
private Object data; //数据 date
public Result() {
}
public Result(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static Result success(Object data){
return new Result(1, "success", data);
}
public static Result success(){
return new Result(1, "success", null);
}
public static Result error(String msg){
return new Result(0, msg, null);
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
分层解耦
三层架构(打点75)
复用性强
便于维护
便于扩展
- controller:控制层,接收前端发送的请求,对请求机型处理,并响应数据。
- service:业务逻辑层,处理具体的业务逻辑。
- dao:数据访问层(持久层),负责数据访问操作,包括数据的增删改查。
IOC和DI
内聚:软件中各个功能模块内部的功能联系
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合。
步骤:
2 .
3. 运行测试
Bean组件扫描
- 前面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描。
- @ComponentScan注解虽然没有显示配置,但是实际上已经包含了启动类声明类声明注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包。
Bean注入
解决方案:
- @Primary
- @Qualifier
- @Resource
- @Resource 与 @Autowired区别
后者是spring框架提供的注解,而前者是JDK提供的注解。
后者默认是按照类型注入,而前者默认是按照名称注入。
Mybaits
持久层框架,简化JDBC的开发
入门
步骤
配置SQL提示
JDBC
数据库连接池
优势:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
lombok
Lombok 是一个使用的java类库,能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化Java开发、提高效率。
lombok会在编译时,自动生成对应的java代码。我们使用的lombok时,还需要安装一个lombok的插件(idea自带)
基础操作
删
如果mapper接口方法形参只有一个普通类型的参数,#{……}里面的属性名可以随便写,如:#{id}
预编译
输出日志
预编译更快更安全,防止sql注入
新增
在EmpMapper中写入以下:
注意驼峰命名
在SpringbootMybatisCrudApplicationTests测试类中填写需要增加的信息。
主键返回
描述:在数据添加成功后,需要获取插入数据库数据的主键。
更新
查询
- 根据ID
第三种要严格符合下划线命名规范。第三种在配置文件中进行。
- 根据条件
参数名说明
XML映射文件
使用Mybatis的注解,主要是来完成一些简单的增删改查功能。如果需要实现复杂的sql功能,建议使用xml来配置映射语句。
官方说明:https://mybatis.net.cn/getting-started.html
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--id="方法名——>EmpMapper" resultType="单条所封装的记录的全类名"-->
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and
entrydate between #{begin} and #{end} order by update_time desc
</select>
</mapper>
动态sql
if
用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接sql。
set
动态地在行首插入SET关键字,并会删掉额外的逗号。(用在update语句中)。
where
where元素指挥在子元素有内容的情况下插入where子句,而且会自动去除子句的开头的AND或OR
foreach
用于批量删除
- 接口方法:
- xml映射文件
sql片段
<sql>
:定义可重用的sql片段
<include>
:通过属性refid,指定包含的sql片段
项目
……前面的部分内容丢失【哭死了!】
配置文件
参数配置化
yml文件配置
ConfigurationProperties配置
解决value(“${}”)只能传入一个值的复用性差的缺点。
创建一个AliOSSProperties的类,并将需要的成员变量分装进去。
@ConfigurationProperties(prefix=“aliyun.oss”)声明自动将yml配置文件中的阿里云中的值,赋值给类中的成员变量。
在AliOSSUtils类别中注入AliOSSPropreties,并创建相关对象调用方法获取对象的值。
登录实现
会话
JWT令牌
- 生成JWT令牌
- 解析JWT令牌的body部分
@Test
public void testParseJwt(){//解析jwt body的内容
Claims claims=Jwts.parser()
.setSigningKey("nytd")
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjg4OTk3MDcyLCJ1c2VybmFtZSI6IlRvbSJ9.-_AAjObuDnlTyREIcVrvq0aIdAVCHjAouqUHAVPZum0")
.getBody();
System.out.println(claims);
}
登录后下发令牌
- 先创建一个JWTUtils类
private static String signKey = "nytd";
private static Long expire= 420000L;
public static String generateJwt(Map<String,Object> claims){//设置Jwt令牌
String jwt=Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256,signKey)
.setExpiration(new Date(System.currentTimeMillis()+expire))
.compact();
return jwt;
}
public static Claims ParseJWT(String jwt){//解析jwt body的内容
Claims claims=Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
- 再对LoginController中登录功能进行优化
@PostMapping("/login")
public Result login(@RequestBody Emp emp){
log.info("登录的用户:{}",emp);
Emp e=empService.login(emp);
if(e!=null){
Map<String,Object> claims =new HashMap<>();
claims.put("id",e.getId());
claims.put("name",e.getName());
claims.put("username",e.getUsername());
String jwt= JWTUtils.generateJwt(claims);
return Result.success(jwt);
}
return Result.error("用户名或者密码错误!");
}
在登录成功后生成JWT令牌,存储在浏览器中的localstorage中,请求方式为token
filter
@WebFilter(urlPatterns = "/")
public class DemoFilter implements Filter {
@Override//初始化方法,只调用一次
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
System.out.println("初始化");
}
@Override//拦截到请求之后调用,调用多次
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("拦截");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override//销毁方法,只能调用一次
public void destroy() {
System.out.println("销毁");
Filter.super.destroy();
}
注意在运行主程序类中要加上一个注解:@ServletComponentScan//开启对servlet的支持
filter放行后会返回filter的执行程序,继续之后的代码
拦截路径
过滤器链
登录效验Filter-流程
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
//1.获取请求url。
String url = req.getRequestURL().toString();
log.info("请求的url:{}",url);
//2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
if(url.contains("login")){
log.info("登录操作,放行……");
filterChain.doFilter(servletRequest,servletResponse);
return;
}
//3.获取请求头中的令牌(token)
String jwt = req.getHeader("token");
//4.判断令牌是否存在,如果不存在,返回错误信息(未登录)。
if(!StringUtils.hasLength(jwt)){
log.info("请求头token为空,返回未登录的信息");
Result error=Result.error("NOT_LOGIN");
//手动转换对象--json---阿里巴巴fastJSON
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
JWTUtils.ParseJWT(jwt);
} catch (Exception e) {
e.printStackTrace();
log.info("解析令牌失败,返回未登录错误信息");
Result error=Result.error("NOT_LOGIN");
//手动转换对象--json---阿里巴巴fastJSON
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//6.放行
log.info("令牌合法,放行");
filterChain.doFilter(servletRequest,servletResponse);
}
}
Interceptor
拦截路径
异常处理
在service层进行实现
@RestControllerAdvice指定返回的数据类型
@ExceptionHandler指定捕获所有异常
rollbackFor
默认情况下,只有RuntimeException才回滚异常。rollbackFor属性用于控制出现何种异常类型,回滚事务。
(rollback=Exception.class)声明所有类型。
propagation
事务传播行为:指的是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。
先在pojo中创建DeptLog的类,在分别创建service和mapper的接口,在deptServiceImpl的实现类中的删除部门中调用DeptLogSerivce中的insert方法,在deptMapper中操作insert语句
AOP
步骤引入AOP的依赖,编写AOP的代码(根据特定的场景)
核心概念
通知类型
- @PointCut
通知的执行顺序
切入点表达式
execution
@annotation
切入点
案例
将案例中的增删改相关接口的操作日志记录到数据库表中
LogAspect.java
@Slf4j
@Component
@Aspect//切面类
public class LogAspect {
@Autowired
private HttpServletRequest request;
@Autowired
private OperateLogMapper operateLogMapper;
@Around("@annotation(com.nytd.anno.Log)")
public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
//操作人ID-当前登录员工ID
//获取请求头中的jwt令牌,解析令牌
String jwt = request.getHeader("token");
Claims claims = JWTUtils.ParseJWT(jwt);
Integer operateUser = (Integer) claims.get("id");
//操作时间
LocalDateTime operateTime=LocalDateTime.now();
//操作类名
String className = joinPoint.getTarget().getClass().getName();
//操作方法名
String methodName = joinPoint.getSignature().getName();
//操作方法参数
Object[] args = joinPoint.getArgs();
String methodsParams = 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(null,operateUser,operateTime,className,methodName,methodsParams,returnValue,costTime);
operateLogMapper.insert(operateLog);
log.info("AOP记录操作日志:{}",operateLog);
return result;
}
}
配置
- 文件的优先级
设置参数
命令行参数>java系统属性>三个配置文件
获取bean对象
bean的作用域