MVC
MVC 就是 Model View Controller 的缩写,属于一种软件架构设计模式一种思想,把我们的项目分为控制器(Controller)、模型(Model)、视图(view)三个部分,model就是处理业务逻辑处理数据的,controller就是接受请求丢给对应的model进行处理的,view就是展示数据的也就是界面
Spring-MVC
Spring MVC(Model-View-Controller)是Spring框架中的一个模块,用于构建基于MVC设计模式的Web应用程序。Spring MVC将应用程序分为三个主要部分:
Model:负责处理数据和业务逻辑。
View:负责展示数据。
Controller:负责处理用户请求并返回响应。
Spring MVC通过一系列的注解(如@Controller、@RequestMapping、@RequestParam等)简化了Web应用程序的开发。
Maven
Apache Maven 是一个项目管理和构建工具,它基于项目对象模型(POM)的概念,通过一小段描述信息来管理项目的构建。
Maven的作用
依赖管理:方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题。
统一项目结构:提供标准、统一的项目结构。
项目构建:标准跨平台(Linux、Windows、MacOS)的自动化项目构建方式。
官网:http://maven.apache.org/
引入依赖,会先查找本地仓库有没有,没有在从远程仓库查找(私服 一般是公司内部有的),没有再从中央仓库查找,再从中央仓库下载到私服,再从私服中下载到本地仓库,再从本地仓库中下载。
Maven的安装
配置Maven环境(全局)
将你所配置的路径输入进去
选择jdk的版本
Maven坐标
2023版idea创建Maven项目
依赖管理
依赖:指当前项目运行所需要的jar包,一个项目中可以引入多个依赖。
引入依赖
<dependencies>
<dependency>...</dependency>
<dependency>...</dependency>
</dependencies>
依赖传递
依赖范围
生命周期
Maven的生命周期就是为了对所有的maven项目构建过程进行抽象和统一。
在同一套生命周期中,当运行后面的阶段时,前面的阶段都会运行。
Maven高级
分模块设计与开发
分模块设计:将项目按照功能拆分成若干个子模块,方便项目的管理维护、扩展,也方便模块间的相互调用,资源共享。
分模块设计需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分
继承与聚合
继承关系实现
版本锁定
在maven中,可以在父工程的pom文件中通过 <dependencyManagement> 来统一管理依赖版本。
子工程引入依赖时,无需指定 <version> 版本号,父工程统一管理。变更依赖版本,只需在父工程中统一变更。
把所有的版本聚到一起,方便查找修改
<dependencies> 是直接依赖,在父工程配置了依赖,子工程会直接继承下来。 <dependencyManagement> 是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)
聚合
聚合:将多个模块组织成一个整体,同时进行项目的构建。
聚合工程 :一个不具有业务功能的“空”工程(有且仅有一个pom文件)
作用: 快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即可)
聚合工程中所包含的模块,在构建时,会自动根据模块间的依赖关系设置构建顺序,与聚合工程中模块的配置书写位置无关。
私服
私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的中央仓库,用于解决团队内部的资源共享与资源同步问题。
私服在企业项目开发中,一个项目/公司,只需要一台即可(无需我们自己搭建,会使用即可)。
资源上传与下载
HTTP协议
概念:Hyper Text Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则
特点:
1、基于TCP协议:面向连接,安全
2、基于请求-响应模型的:一次请求对应一次响应
3、HTTP协议是无状态的协议:对于事务处理没有记忆能力。每次请求-响应都是独立的。
缺点:多次请求间不能共享数据。
优点:速度快。
HTTP-请求协议
请求行:请求数据第一行 (请求方式、资源路径、协议)
请求头:第二行开始,格式key:value
请求体:POST请求,存放请求参数
HTTP-响应协议
响应行:响应数据第一行(协议、状态码、描述
响应头:第二行开始,格式key:value
响应体:最后一部分,存放响应数据
HTTP响应格式
HTTP-协议解析
浏览器内置了解析HTTP-协议的程序
客户端借助web服务器 进行操作,响应数据
Web 服务器
Web服务器是一个软件程序,对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是 "提供网上信息浏览服务" 。
web服务器
对HTTP协议操作进行封装,简化web程序开发。
部署web项目,对外提供网上信息浏览服务。
Tomcat
一个轻量级的web服务器,支持servlet、jsp等少量javaEE规范。
也被称为web容器、servlet容器。
SpringBootWeb
Spring Boot 可以帮助我们非常快速的构建应用程序、简化开发、提高效率。
创建springboot工程:
创建springboot工程,并勾选web开发相关依赖。
Springboo内嵌Tomcat服务器
起步依赖:
spring-boot-starter-web:包含了web应用开发所需要的常见依赖。
spring-boot-starter-test:包含了单元测试所需要的常见依赖。
内嵌Tomcat服务器
基于Springboot开发的web应用程序,内置了tomcat服务器,当启动类运行时,会自动启动内嵌的tomcat服务器。
注解
@SpringBootApplication
作用:这是一个组合注解,包括了@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解。用于标识SpringBoot应用程序的入口类。
@Configuration:指示这个类是一个配置类,它定义了一个或多个@Bean方法,用于创建和配置Spring应用程序上下文中的Bean。
@EnableAutoConfiguration:启用Spring Boot的自动配置机制,它会自动添加所需的依赖项和配置,以使应用程序能够运行。
@ComponentScan:指示Spring Boot扫描当前包及其子包中的所有@Component、@Service、@Repository和@Controller注解的类,并将它们注册为Spring Bean。
@SpringBootApplication注解通常被用于Spring Boot应用程序的入口类上,用于启动Spring Boot应用程序。它可以简化Spring应用程序的配置和启动过程。
用例:
@RestController
@RestController
@RestController的作用等同于@Controller + @ResponseBody @Controller注解,表明了这个类是一个控制器类
@RequestMapping
@RequestMapping
@RequestParam
@RequestParam
@DateTimeFormat:完成日期参数格式转换
@DateTimeFormat
@RequestBody
@RequestBody
@PathVariable
@PathVariable
@Component
@Autowired
@ComponentScan:@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication 中,默认扫描的范围是启动类所在包及其子包。
@SpringBootApplication具有包扫描作用,默认扫描当前包及其子包
@Component,@Controller,@Service,@Repository
请求响应
请求(HttpServletRequest):获取请求数据
响应(HttpServletResponse):设置响应数据
请求
postman:常用于后端测试
Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。
作用:常用于进行接口测试
简单参数
原始方式
// 1. 简单参数//原始方式@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方式
简单参数:参数名与形参变量名相同,定义形参即可接收参数。
// springboot方式@RequestMapping("/simpleParam")public String simpleParam(String name, Integer age){System.out.println(name+ ":" + age);return "OK";}
简单参数:如果方法形参名称与请求参数名称不匹配,可以使用 @RequestParam 完成映射。
@RequestMapping("/simpleParam")public String simpleParam(@RequestParam(name = "name", required = false) String username, Integer age){System.out.println(username+ ":" + age);return "OK";}
@RequestParam中的required属性默认为true,代表该请求参数必须传递,如果不传递将报错。 如果该参数是可选的,可以将required属性设置为false。
实体参数
简单实体对象:请求参数名与形参对象属性名相同,定义POJO接收即可
public class User {private String name;private Integer age;
}
//2. 实体参数@RequestMapping("/simplePojo")public String simplePojo(User user){System.out.println(user);return "OK";}
复杂实体对象:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
public class Address {private String province;private String city;
}
public class User {private String name;private Integer age;private Address address;
}
@RequestMapping("/complexPojo")public String complexPojo(User user){System.out.println(user);return "OK";}
数组集合参数
数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数
//3. 数组集合参数@RequestMapping("/arrayParam")public String arrayParam(String[] hobby){System.out.println(Arrays.toString(hobby));return "OK";}//集合来接收@RequestMapping("/listParam")public String listParam(@RequestParam List<String> hobby){System.out.println(hobby);return "OK";}
数组:请求参数名与形参中数组变量名相同,可以直接使用数组封装
集合:请求参数名与形参中集合变量名相同,通过@RequestParam绑定参数关系
日期参数
//4. 日期时间参数@RequestMapping("/dateParam")public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime){System.out.println(updateTime);return "OK";}
Json参数
JSON参数:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数,需要使用 @RequestBody 标识
public class Address {private String province;private String city;
}
public class User {private String name;private Integer age;private Address address;
}
//5. json参数@RequestMapping("/jsonParam")public String jsonParam(@RequestBody User user){System.out.println(user);return "OK";}
路径参数
路径参数:通过请求URL直接传递参数,使用{…}来标识该路径参数,需要使用 @PathVariable 获取路径参数
//6. 路径参数@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";}
响应
Result类
package com.itheima.pojo;/*** 统一响应结果封装类*/
public class Result {private Integer code ;//1 成功 , 0 失败private String msg; //提示信息private Object data; //数据 datepublic 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);}@Overridepublic String toString() {return "Result{" +"code=" + code +", msg='" + msg + '\'' +", data=" + data +'}';}
}
@RequestMapping("/hello")public Result hello(){System.out.println("Hello World ~");//return new Result(1,"success","Hello World ~");return Result.success("Hello World ~");}@RequestMapping("/getAddr")public Result getAddr(){Address addr = new Address();addr.setProvince("广东");addr.setCity("深圳");return Result.success(addr);}@RequestMapping("/listAddr")public Result listAddr(){List<Address> list = new ArrayList<>();Address addr = new Address();addr.setProvince("广东");addr.setCity("深圳");Address addr2 = new Address();addr2.setProvince("陕西");addr2.setCity("西安");list.add(addr);list.add(addr2);return Result.success(list);
分层解耦
三层架构
controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据。
service:业务逻辑层,处理具体事务的逻辑。
dao:数据访问层(Data Access Object)(持久层),负责数据访问操作,包括数据的增删改查。
分层解耦
内聚:软件中各个功能模块内部的功能联系。
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合
控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。(@Component)
依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。(@Autowired)
Bean对象:IOC容器中创建、管理的对象,称之为bean。
IOC & DI
IOC
①. Service层 及 Dao层的实现类,交给IOC容器管理。
②. 为Controller及Service注入运行时,依赖的对象。
③. 运行测试。
DI
登录
登录认证
如果没有登录校验,在未登录情况下,我们也可以直接访问部门管理、员工管理等功能。这是不正常的,需要进行登录校验。
登录校验
所谓登录校验,指的是我们在服务器端接收到浏览器发送过来的请求之后,首先我们要对请求进行 校验。先要校验一下用户登录了没有,如果用户已经登录了,就直接执行对应的业务操作就可以 了;如果用户没有登录,此时就不允许他执行相关的业务操作,直接给前端响应一个错误的结果, 最终跳转到登录页面,要求他登录成功之后,再来访问对应的数据。
HTTP协议是无状态的,下一次请求不会携带上一次请求的信息。
所谓无状态,指的是每一次请求都是独立的,下一次请求并不会携带上一次请求的数据。而浏览器与服 务器之间进行交互,基于HTTP协议也就意味着现在我们通过浏览器来访问了登陆这个接口,实现了登陆 的操作,接下来我们在执行其他业务操作时,服务器也并不知道这个员工到底登陆了没有。因为HTTP协 议是无状态的,两次请求之间是独立的,所以是无法判断这个员工到底登陆了没有。
标记:用户登录成功之后,每一次请求中,都可以获取到该标记。
统一拦截:过滤器Filter 拦截器Interceptor
会话技术
会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪方案:
客户端会话跟踪技术:Cookie
服务端会话跟:Session
令牌技术
Cookie
cookie 是客户端会话跟踪技术,它是存储在客户端浏览器的,我们使用 cookie 来跟踪会话,我们 就可以在浏览器第一次发起请求来请求服务器的时候,我们在服务器端来设置一个cookie。 比如第一次请求了登录接口,登录接口执行完成之后,我们就可以设置一个cookie,在 cookie 当中 我们就可以来存储用户相关的一些数据信息。比如我可以在 cookie 当中来存储当前登录用户的用户 名,用户的ID。 服务器端在给客户端在响应数据的时候,会自动的将 cookie 响应给浏览器,浏览器接收到响应回来 的 cookie 之后,会自动的将 cookie 的值存储在浏览器本地。接下来在后续的每一次请求当中, 都会将浏览器本地所存储的 cookie 自动地携带到服务端。接下来在服务端我们就可以获取到 cookie 的值。我们可以去判断一下这个 cookie 的值是否存 在,如果不存在这个cookie,就说明客户端之前是没有访问登录接口的;如果存在 cookie 的值,就 说明客户端之前已经登录完成了。这样我们就可以基于 cookie 在同一次会话的不同请求之间来共享 数据。
用了 3 个自动:
服务器会 自动 的将 cookie 响应给浏览器。
浏览器接收到响应回来的数据之后,会 自动 的将 cookie 存储在浏览器本地。
在后续的请求当中,浏览器会 自动 的将 cookie 携带到服务器端
Session,它是服务器端会话跟踪技术,所以它是存储在服务器端的。而 Session 的底层其实就是 Cookie 来实现的。
获取Session
如果我们现在要基于 Session 来进行会话跟踪,浏览器在第一次请求服务器的时候,我们就可 以直接在服务器当中来获取到会话对象Session。如果是第一次请求Session ,会话对象是不存 在的,这个时候服务器会自动的创建一个会话对象Session 。而每一个会话对象Session ,它 都有一个ID(示意图中Session后面括号中的1,就表示ID),我们称之为 Session 的ID。
响应Cookie (JSESSIONID)
接下来,服务器端在给浏览器响应数据的时候,它会将 Session 的 ID 通过 Cookie 响应给 浏览器。其实在响应头当中增加了一个 Set-Cookie 响应头。这个 Set-Cookie 响应头对 应的值是不是cookie? cookie 的名字是固定的 JSESSIONID 代表的服务器端会话对象 Session 的 ID。浏览器会自动识别这个响应头,然后自动将Cookie存储在浏览器本地。
查找Session
接下来,在后续的每一次请求当中,都会将 Cookie 的数据获取出来,并且携带到服务端。接下 来服务器拿到JSESSIONID这个 Cookie 的值,也就是 Session 的ID。拿到 ID 之后,就会 从众多的 Session 当中来找到当前请求对应的会话对象Session。
优缺点
优点:Session是存储在服务端的,安全
缺点: 服务器集群环境下无法直接使用Session 移动端APP(Android、IOS)中无法使用Cookie 用户可以自己禁用Cookie Cookie不能跨域
JWT令牌
JWT令牌本质就是一个 字符串。
如果通过令牌技术来跟踪会话,我们就可以在浏览器发起请求。在请求登录接口的时候,如果登录成功,我就可以生成一个令牌,令牌就是用户的合法身份凭证。接下来我在响应数据的时候,我就可以直 接将令牌响应给前端。 接下来我们在前端程序当中接收到令牌之后,就需要将这个令牌存储起来。这个存储可以存储在 cookie 当中,也可以存储在其他的存储空间(比如:localStorage)当中。 接下来,在后续的每一次请求当中,都需要将令牌携带到服务端。携带到服务端之后,接下来我们就需 要来校验令牌的有效性。如果令牌是有效的,就说明用户已经执行了登录操作,如果令牌是无效的,就 说明用户之前并未执行登录操作。 此时,如果是在同一次会话的多次请求之间,我们想共享数据,我们就可以将共享的数据存储在令牌当 中就可以了。
JWT全称:JSON Web Token
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字 签名的存在,这些信息是可靠的。
jwt就是将原始的json数据格式进行了安全的封装,这样就可以直接基于jwt在 通信双方安全的进行信息传输了。
JWT的组成: (JWT令牌由三个部分组成,三个部分之间使用英文的点来分割)
第一部分:Header(头), 记录令牌类型、签名算法等。 例如: {"alg":"HS256","type":"JWT"}
第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如: {"id":"1","username":"Tom"}
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加 入指定秘钥,通过指定签名算法计算而来。
签名的目的就是为了防jwt令牌被篡改,而正是因为jwt令牌最后一个部分数字签名的存在, 所以整个jwt 令牌是非常安全可靠的。一旦jwt令牌当中任何一个部分、任何一个字符被篡 改了,整个令牌在校验的时候都会失败,所以它是非常安全可靠的。
JWT是如何将原始的JSON格式数据,转变为字符串的呢?
其实在生成JWT令牌时,会对JSON格式的数据进行一次编码:进行base64编码 Base64:是一种基于64个可打印的字符来表示二进制数据的编码方式。既然能编码,那也就意味 着也能解码。所使用的64个字符分别是A到Z、a到z、 0- 9,一个加号,一个斜杠,加起来就是 64个字符。任何数据经过base64编码之后,最终就会通过这64个字符来表示。当然还有一个符 号,那就是等号。等号它是一个补位的符号 需要注意的是Base64是编码方式,而不是加密方式。
JWT令牌最典型的应用场景就是登录认证:
1. 在浏览器发起请求来执行登录操作,此时会访问登录的接口,如果登录成功之后,我们需要生成 一个jwt令牌,将生成的 jwt令牌返回给前端。
2. 前端拿到jwt令牌之后,会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服 务端。
3. 服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接 拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处 理。
在JWT登录认证的场景中我们发现,整个流程当中涉及到两步操作:
1. 在登录成功之后,要生成令牌。
2. 每一次请求当中,要接收令牌并对令牌进行校验。 稍后我们再来学习如何来生成jwt令牌,以及如何来校验jwt令牌。
JWT生成和校验
导入依赖
<!-- JWT依赖-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>
生成JWT令牌
public void testGenJWT(){Map<String,Object> claims=new HashMap<>();claims.put("id",1);claims.put("name","tom");String jwt=Jwts.builder()//builder生成.signWith(SignatureAlgorithm.HS256,"itheima")//签名算法 和密钥.setClaims(claims)//自定义内容(载荷).setExpiration(new Date(System.currentTimeMillis()+3600*1000))//设置有效期为1小时.compact();//调用该方法就可以返回一个字符串类型的返回值System.out.println(jwt);}
JWT令牌解析
@Testpublic void parsejwt(){Claims claims= Jwts.parser()//parser解析.setSigningKey("itheima")//签名密钥.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTcyNDg0NjA1NH0.vUHKv48DmnhGiBU3e6o5-SgRBTwIWoM7X6WoQQQd8MU")//JWT令牌.getBody();System.out.println(claims);}
JwtUtils类
public class JwtUtils {private static String signKey = "itheima";private static Long expire = 43200000L;/*** 生成JWT令牌* @param claims JWT第二部分负载 payload 中存储的内容* @return*/public static String generateJwt(Map<String, Object> claims){String jwt = Jwts.builder().addClaims(claims).signWith(SignatureAlgorithm.HS256, signKey).setExpiration(new Date(System.currentTimeMillis() + expire)).compact();return jwt;}/*** 解析JWT令牌* @param jwt JWT令牌* @return JWT第二部分负载 payload 中存储的内容*/public static Claims parseJWT(String jwt){Claims claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(jwt).getBody();return claims;}
}
登录校验
@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("用户名或密码错误");}
过滤器Filter
Filter表示过滤器,是 JavaWeb三大组件(Servlet、Filter、Listener)之一。
过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能 使用了过滤器之后,要想访问web服务器上的资源,必须先经过滤器,过滤器处理完毕之后, 才可以访问对应的资源。过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
Filter快速入门
第1步,定义过滤器 :1.定义一个类,实现 Filter 接口,并重写其所有方法。
第2步,配置过滤器:Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支持。
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {@Override//初始化,只会调用一次public void init(FilterConfig filterConfig) throws ServletException {System.out.println("init 初始化方法执行了");}@Override//拦截到请求之后调用,调用多次public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("Demo 拦截到了放行。。。之前的请求");//放行filterChain.doFilter(servletRequest, servletResponse);System.out.println("Demo 拦截到了放行。。。之后的请求");}@Override//销毁方法,只调用一次public void destroy() {System.out.println("destory 销毁化方法执行了");}
}
@ServletComponentScan //开启servlet组件的支持
@SpringBootApplication
public class TliasWebManagement2Application {public static void main(String[] args) {SpringApplication.run(TliasWebManagement2Application.class, args);}}
执行流程
拦截路径
过滤器链
先要执行过滤器2放行之后的逻辑,再来执行过滤器1放行之后的逻辑,最后在给浏览器响应数据。 以注解方式配置的Filter过滤器,它的执行优先级是按时过滤器类名的 自动排序确定的,类名排名越靠前,优先级越高。
登录校验-Filter
LoginCheckFilter
@Slf4j
@WebFilter("/*")
public class LoginCheckFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest req=(HttpServletRequest)servletRequest;HttpServletResponse resq=(HttpServletResponse)servletResponse;//1、获取请求urlString url=req.getRequestURI();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格式 Controller层有@RestController注解 可以返回JSON格式String notLogin= JSONObject.toJSONString(error);servletResponse.getWriter().write(notLogin);return ;}//5、解析token,如果解析失败,返回错误结果(未登录)try {JwtUtils.parseJWT(jwt);} catch (Exception e) {//jwt 解析失败log.info("解析令牌失败,返回未登录错误信息");Result error= Result.error("NOT_LOGIN");//手动转换成json格式 Controller层有@RestController注解 可以返回JSON格式String notLogin= JSONObject.toJSONString(error);servletResponse.getWriter().write(notLogin);return ;}//6、放行log.info("令牌合法,放行");filterChain.doFilter(servletRequest,servletResponse);}
}
拦截器Interceptor
拦截器是一种动态拦截方法调用的机制,类似于过滤器。
拦截器是Spring框架中提供的,用来动态拦截控制器方法的执行。
拦截器的作用: 拦截请求,在指定方法调用前后,根据业务需要执行预先设定的代码。
拦截器的使用步骤:1. 定义拦截器 2. 注册配置拦截器
自定义拦截器:实现HandlerInterceptor接口,并重写其所有方法
//自定义拦截器
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {//目标资源方法执行前执行。 返回true:放行 返回false:不放行@Overridepublic boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle .... ");return true; //true表示放行}//目标资源方法执行后执行@Overridepublic void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler, ModelAndView
modelAndView) throws Exception {System.out.println("postHandle ... ");}//视图渲染完毕后执行,最后执行@Overridepublic void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws
Exception {System.out.println("afterCompletion .... ");}
}
注册配置拦截器:实现WebMvcConfigurer接口,并重写addInterceptors方法(相当于Filter中的unit方法初始化)
@Configuration
public class WebConfig implements WebMvcConfigurer {//自定义的拦截器对象@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**
");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)}
}
拦截路径
addPathPatterns("要拦截路径") 方法,就可以指定要拦截哪些资源
excludePathPatterns("不拦截路径") 方法,指定哪些 资源不需要拦截。
登录校验- Interceptor
登录校验拦截器
//还是很好理解的 首先这一个拦截器的逻辑 然后你肯定要把这个拦截器在系统启用 所以需要一个注册
//定义拦截器
//快捷键 Ctrl+o
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {@Override//目标资源方法运行前运行,返回true,放行,放回false,不放行public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {//1、获取请求urlString url=req.getRequestURI();log.info("请求的url:{}",url);//2、判断请求url中是否包含login,如果包含,说明是登录操作,放行if(url.contains("login")){log.info("登录操作,放行...");return true;}//3、获取请求头中的令牌(token)String jwt= req.getHeader("token");//4、判断令牌是否存在,如果不存在,返回错误结果(未登录)if(!StringUtils.hasLength(jwt)){log.info("请求头token为空,返回未登录的信息");Result error= Result.error("NOT_LOGIN");//手动转换成json格式 Controller层有@RestController注解 可以返回JSON格式String notLogin= JSONObject.toJSONString(error);resp.getWriter().write(notLogin);return false;}//5、解析token,如果解析失败,返回错误结果(未登录)try {JwtUtils.parseJWT(jwt);} catch (Exception e) {//jwt 解析失败log.info("解析令牌失败,返回未登录错误信息");Result error= Result.error("NOT_LOGIN");//手动转换成json格式 Controller层有@RestController注解 可以返回JSON格式String notLogin= JSONObject.toJSONString(error);resp.getWriter().write(notLogin);return false;}//6、放行log.info("令牌合法,放行");return true;}@Override//资源方法运行后运行public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle...");}@Override//视图渲染完毕后运行,最后运行public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion...");}
}
注册配置拦截器
@Configuration //加上之后 代表当前类是配置类
public class WebConfig implements WebMvcConfigurer {@AutowiredLoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");}
}
执行流程
当我们打开浏览器来访问部署在web服务器当中的web应用时,此时我们所定义的过滤器会拦截到 这次请求。拦截到这次请求之后,它会先执行放行前的逻辑,然后再执行放行操作。而由于我们当 前是基于springboot开发的,所以放行之后是进入到了spring的环境当中,也就是要来访问我 们所定义的controller当中的接口方法。
Tomcat并不识别所编写的Controller程序,但是它识别Servlet程序,所以在Spring的Web环 境中提供了一个非常核心的Servlet:DispatcherServlet(前端控制器),所有请求都会先 进行到DispatcherServlet,再将请求转给Controller。
当我们定义了拦截器后,会在执行Controller的方法之前,请求被拦截器拦截住。执行 preHandle() 方法,这个方法执行完成后需要返回一个布尔类型的值,如果返回true,就表示放 行本次操作,才会继续访问controller中的方法;如果返回false,则不会放行(controller 中的方法也不会执行)。
在controller当中的方法执行完毕之后,再回过来执行 postHandle() 这个方法以及 afterCompletion() 方法,然后再返回给DispatcherServlet,最终再来执行过滤器当中放 行后的这一部分逻辑的逻辑。执行完毕之后,最终给浏览器响应数据。
它们之间的区别主要是两点:
接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资 源。
MYSQL
数据库:DataBase(DB),是存储和管理数据的仓库
数据库管理系统:DataBase Management System (DBMS),操纵和管理数据库的大型软件。
SQL:Structured Query Language,操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准。
mysql连接
DDL:Data Definition Language 数据定义语言,用来定义数据库对象(数据库,表,字段)
DML: Data Manipulation Language 数据操作语言,用来对数据库表中的数据进行增删改
DQL: Data Query Language 数据查询语言,用来查询数据库中表的记录
DCL: Data Control Language 数据控制语言,用来创建数据库用户、控制数据库的访问权限
DDL
DDL(数据库操
作)
DDL(表操作)
DML
添加数据(INSERT) 修改数据(UPDATE) 删除数据(DELETE)
insert into 表名 属性 values()
update 表名 set 属性=值1... [where 条件]
修改数据:update 表名 set 字段名1 = 值1 , 字段名2 = 值2 , .... [ where 条件 ]
delete from 表名 [where 条件]
删除数据:delete from 表名 [ where 条件 ]
DQL
DQL英文全称是Data Query Language(数据查询语言),用来查询数据库表中的记录。
关键字:SELECT
where与having区别:
执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
判断条件不同:where不能对聚合函数进行判断,而having可以
分组排序:ASC:升序(默认值) DESC:降序
Mybatis
@Mapper 在运行时,mybatis框架会自动生成该接口的实现类对象(代理对象),并且将该对象交给IOC容器管理
# 配置数据库的连接信息 - 四要素
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=1234#配置mybatis的日志 指定输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
增删改
@Mapper
public interface EmpMapper {//删除操作@Delete("delete from emp where id=#{id} ")public void delete(Integer id);//增加操作@Options(keyProperty = "id",useGeneratedKeys = true)//会自动将生成的主键值,赋值给emp对象的id属性@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +"values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime});")public void insert(Emp emp);//更新操作@Update("update emp set username=#{username},name=#{name},gender=#{gender},image=#{image},job=#{job},entrydate=#{entrydate},dept_id=#{deptId},update_time=#{updateTime} where id=#{id}")public void update(Emp emp);
}
查询
在配置里添加上
#开启mybatis的驼峰命名自动映射开关 a_column----->aColumn
mybatis.configuration.map-underscore-to-camel-case=true
// @Select("select * from emp where id=#{id}")
// public Emp getById(Integer id);// 一、给字段起别名,让别名与实体类属性一致@Select("select id,username,password,name,gender,image,job,entrydate,dept_id deptId,create_time createTime,update_time updateTime from emp where id=#{id}")public Emp getById(Integer id);// 二、通过@Results,@Results注解手动映射封装@Results({@Result(column = "dept_id",property = "deptId"),@Result(column = "create_time",property = "createTime"),@Result(column = "update_time",property = "updateTime")})@Select("select * from emp where id=#{id}")public Emp getById(Integer id);//三、开启mybatis的驼峰命名自动映射开关----dept_id---->deptId 在环境配置中添加@Select("select * from emp where id=#{id}")public Emp getById(Integer id);
contat连接
@Select("select * from emp where name like '%${name}%' and gender=#{gender} and entrydate between #{begin} and #{end} order by update_time desc")public List<Emp> list(String name, Short gender, LocalDate begin,LocalDate end);@Select("select * from emp where name like concat('%',#{name},'%') and gender=#{gender} and entrydate between #{begin} and #{end} order by update_time desc")public List<Emp> list(@Param("name") String name,@Param("gender") Short gender, @Param("begin") LocalDate begin,@Param("end") LocalDate end);
XML映射文件
MybatisX 是一款基于 IDEA 的快速开发Mybatis的插件,为效率而生
使用Mybatis的注解,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句。
Mybatis动态SQL
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--XML映射文件定义规范1 XML文件的名称与Mapper接口名称一致,并且放置在相同的包下(同包同名)2 XML文件的namespace属性为Mapper接口全限定名一致3 XML文件中sql语句中id与Mapper接口中的方法名一致
-->
<mapper namespace="com.itheima.mapper.EmpMapper"><!--<sql>:定义可重用的SQL片段<include>:通过属性refid,指定包含的sql片段--><sql id="commonSelect">select id,username,password,name,gender,image,job,entrydate,dept_id,create_time,update_timefrom emp</sql><update id="update2">update emp<set><if test="username!=null">username=#{username},</if><if test="name!=null">name=#{name},</if><if test="gender!=null">gender=#{gender},</if><if test="image!=null">image=#{image},</if><if test="job!=null">job=#{job},</if><if test="entrydate!=null">entrydate=#{entrydate},</if><if test="deptId!=null">dept_id=#{deptId},</if><if test="updateTime!=null">update_time=#{updateTime}</if></set>where id=#{id}</update><!--2--><!--resultType:单条记录所封装的类型--><!--<if>:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL<where>:where元素只会在子元素有内容的情况下才会插入where字句。而且会自动去除子句的开头and或or-->
<select id="list" resultType="com.itheima.pojo.Emp"><!--3--><include refid="commonSelect"/><where><if test="name != null">name like concat('%',#{name},'%')</if><if test="gender !=null">and gender=#{gender}</if><if test="begin!=null and end!=null">and entrydate between #{begin} and #{end}</if></where>order by update_time desc</select>
<!--批量删除员工 delete from emp where id in (13,14,15)--><!--collection:遍历的集合item:遍历出来的元素sepatator:分隔符open:遍历开始前拼接的SQL片段close:遍历结束后拼接的SQL片段--><delete id="deleteById">delete from emp where id in<foreach collection="list" item="id" separator="," open="(" close=")">#{id}</foreach></delete></mapper>