目录
[REST(REpresentation State Transfer),表述性状态转换,它是一种软件架构风格](#REST(REpresentation State Transfer),表述性状态转换,它是一种软件架构风格)
1.准备工作
环境搭建
开发规范
REST(REpresentation State Transfer),表述性状态转换,它是一种软件架构风格
注意事项
- REST是风格,是约定方式,约定不是规定,可以打破
- 描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非单个资源。如:users、emps、books
统一响应结果
- 前后端交互统一响应结果Result
java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Integer code;//响应码,1 代表成功; 0 代表失败
private String msg; //响应信息 描述字符串
private Object data; //返回的数据
//增删改 成功响应
public static Result success(){
return new Result(1,"success",null);
}
//查询 成功响应
public static Result success(Object data){
return new Result(1,"success",data);
}
//失败响应
public static Result error(String msg){
return new Result(0,msg,null);
}
}
2.部门管理功能
查询部门
java
/**
* 部门管理Controller
*/
@Slf4j
@RestController
public class DeptController {
// private static Logger log = LoggerFactory.getLogger(DeptController.class);
@Autowired
private DeptService deptService;
// @RequestMapping(value = "/depts",method = RequestMethod.GET) //指定请求方式为GET
@GetMapping("/depts")
public Result list(){
log.info("查询全部部门数据");
//调用service查询部门数据
List deptList = deptService.list();
return Result.success(deptList);
}
}
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Override
public List list() {
return deptMapper.list();
}
}
@Mapper
public interface DeptMapper {
/**
* 查询全部部门
* @return
*/
@Select("select * from dept")
List list();
}
删除部门
java
/**
* 删除部门数据
*/
@DeleteMapping("/depts/{id}")
public Result delete(@PathVariable Integer id){
log.info("根据id删除部门:{}",id);
DeptService.delete(id);
return Result.success();
}
@Override
public void delete(Integer id) {
deptMapper.deleteById(id);
}
/**
* 根据id删除部门
* @param id
*/
@Delete("delete from dept where id = #{id}")
void deleteById(Integer id);
新增部门
java
/**
* 新增部门数据
*/
@PostMapping("/depts")
public Result add(@RequestBody Dept dept){
log.info("新增部门:{}" , dept);
deptService.add(dept);
return Result.success();
}
@Override
public void add(Dept dept) {
dept.setCreateTime(LocalDateTime.now());
dept.setUpdateTime(LocalDateTime.now());
deptMapper.insert(dept);
}
@Insert("insert into mybatis.dept(name, create_time, update_time ) values (#{name},#{createTime},#{updateTime})")
void insert(Dept dept);
@RequestMapping
3.员工管理功能
分页查询
java
@Slf4j
@RestController
public class EmpController {
@Autowired
private EmpService empService;
@GetMapping("/emps")
public Result page(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize){
log.info("分页查询,参数:{},{}",page,pageSize);
//调用service分页查询
PageBean pageBean = empService.page(page,pageSize);
return Result.success(pageBean);
}
}
@Service
public class EmpServiceImpl implements EmpService {
@Autowired
private EmpMapper empMapper;
@Override
public PageBean page(Integer page, Integer pageSize) {
//1.获取总记录数
Long count = empMapper.count();
//2.获取分页查询结果列表
Integer start = (page - 1) * pageSize;
List<Emp> empList = empMapper.page(start, pageSize);
//3.封装PageBean对象
PageBean pageBean = new PageBean(count, empList);
return pageBean;
}
}
@Mapper
public interface EmpMapper {
/**
* 查询总记录数
* @return
*/
@Select("select count(*) from emp")
public Long count();
/**
* 分页查询,获取列表数据
* @param start
* @param pageSize
* @return
*/
@Select("select * from emp limit #{start},#{pageSize}")
public List<Emp> page(Integer start,Integer pageSize);
}
批量删除员工
新增员工
文件上传
简介
- 文件上传,是指将本地图片、视频、音频等文件上传到服务器,供其他用户浏览或下载的过程
前端页面三要素
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
姓名: <input type="text" name="username"><br>
年龄: <input type="text" name="age"><br>
头像: <input type="file" name="image"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
服务端接口
java
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image){
log.info("文件上传:{},{},{}" ,username,age,image);
return Result.success();
}
本地存储
- 在服务端,接收到上传来的文件之后,将文件存储在本地服务器磁盘中
java
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) throws IOException {
log.info("文件上传:{},{},{}" ,username,age,image);
//获取原始文件名
String originalFilename = image.getOriginalFilename();
//构造唯一的文件名 - uuid(通用唯一识别码)
int index = originalFilename.lastIndexOf(".");
String extname = originalFilename.substring(index);
String newFileName = UUID.randomUUID().toString()+ extname;
log.info("新的文件名: {}" ,newFileName);
//将文件存储在服务器的磁盘目录中E:\images
image.transferTo(new File("E:\\images\\" + newFileName));
return Result.success();
}
在Springboot中,文件上传,默认单个文件允许最大大小为1M。如果需要上传大文件,可以进行如下配置:
XML
#配置单个文件上传大小限制
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大大小的限制(一次请求中是可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB
修改员工
查询回显
修改员工
参数配置化
配置格式
yml数据格式
XML
#定义对象/Map集合
user:
name:Tom
age:20
address:beijing
#定义数组/List/Set
hobby:
- java
- C
- game
- sport
4.用户登录功能
登录功能
java
@RestController
@Slf4j
public class loginController {
private EmpService empService;
@PostMapping("/login")
public Result login(@RequestBody Emp emp){
log.info("员工登录:{}",emp);
Emp e = empService.login(emp);
return e != null ?Result.success():Result.error("用户名或密码错误");
}
}
@Override
public Emp login(Emp emp) {
return empMapper.getByUsernameAndPassword(emp);
}
/**
* 根据用户名和密码查询员工
* @param emp
* @return
*/
@Select("select * from emp where username = #{usernae} and password = #{password}")
Emp getByUsernameAndPassword(Emp emp);
}
登录校验
会话技术
- 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。
- 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
- 会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
- 令牌技术(企业主流)
会话跟踪方案对比
JWT令牌
- 全称:JSON Web Token
- 定义了一张简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息
- 组成:
- 第一部分:Header(头),记录令牌类型、签名算法
- 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息
- 第三部分:Signature(签名),防止Token被篡改、确保安全性
- 场景:登录认证
- ①登录成功后,生成令牌
- ②后续每个请求,都有携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理
JWT-生成
JWT-校验
登录-生成令牌
java
@Autowired
private EmpService empService;
@PostMapping("/login")
public Result login(@RequestBody Emp 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());
//生成Jwt令牌
String jwt = JwtUtils.generateJwt(claims);
return Result.success(jwt);
}
return Result.error("错误");
}
过滤器Filter
- 概念:是JavaWeb三大组件之一
- 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
入门程序
java
@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("拦截到了请求");
//放行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override //销毁方法,只调用一次
public void destroy() {
System.out.println("destroy 销毁方法执行了");
}
}
FIlter执行流程
Filter拦截路径
过滤器链
- 介绍:一个Web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链
- 顺序:注解配置的Filter,优先级是按照过滤器类名的自然排序
登录校验Filter
java
@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.getRequestURI().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
- 概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架提供的,用来动态拦截控制器方法的执行
快速入门
拦截器-拦截路径
拦截器-执行流程
- 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口
- 拦截范围不同:过滤器会拦截所有的资源,而拦截器只会拦截Spring环境的资源
登录校验Interceptor
java
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override //目标资源方法运行前运行,返回true:放行,返回false,不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
//1.获取请求url
String url = req.getRequestURI().toString();
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 ---------> 阿里巴巴fastJSON
String notLogin = JSONObject.toJSONString(error);
return false;
}
//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);
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...");
}
}
异常处理
java
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) //捕获所有异常
public Result ex(Exception ex){
ex.printStackTrace();
return Result.error("对不起,操作失败,请联系管理员");
}
}
5.事务管理
Spring事务管理
- 注解:@Transactional
- 位置:业务层的方法上、类上、接口上
- 作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务
java
@Transactional //spring事务管理
@Override
public void delete(Integer id) {
deptMapper.deleteById(id);
int i = 1/0;
empMapper.deleteByDeptId(id);
}
事务属性-回滚
rollbackFor:默认情况下,只有出现运行时异常RuntimeException才回滚异常。rollbackFor属性用于控制出现何种异常类型,回滚事务。
java
@Transactional(rollbackFor = Exception.class) //spring事务管理
@Override
public void delete(Integer id) {
deptMapper.deleteById(id);
int i = 1/0;
empMapper.deleteByDeptId(id);
}
事务属性-传播行为
propagation:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制
场景
- REQUIRED:大部分情况下都是用该传播行为
- REQUIRED_NEW:当我们不希望事务之间相互影响时,可以使用。比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功
6.AOP(面向切面编程)
AOP概述
- AOP:Aspect Oriented Programing,其实就是面向特定方法编程
- 场景:
- 实现:
- 动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程
AOP核心概念
- 连接点:JoinPoint,可以被AOP控制的方法
- 通知:Advice,指哪些重复的逻辑,也就是共性功能
- 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
- 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
通知类型
@PointCut
- 该注解的作用是将公共的切入点表达式抽取出来,需要用到时引用该切点表达式即可
通知顺序
切入点表达式
- 描述切入点方法的一种表达式
- 作用:主要用来决定项目中的哪些方法需要加入通知
- 常见形式:
- 1.execution:根据方法的签名来匹配
- 2.@annotation:根据注解匹配
execution表达式
@Annotation注解表达式
连接点
- 在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等
- 对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint
- 对于其他四种通知,获取连接点只能使用JoinPoint,它是ProceedingJoinPoint的父类型
7.配置优先级
8.Bean管理
获取bean
- 默认情况下,Spring项目启动时,会把bean都创建好放在IOC容器中,如果想要主动获取这些bean,可以通过如下方式:
java
@Autowired
private ApplicationContext applicationContext; //IOC容器对象
//获取bean对象
@Test
public void testGetBean(){
//根据bean的名称获取
DeptController bean1 = (DeptController) applicationContext.getBean("deptController");
System.out.println(bean1);
//根据bean的类型获取
DeptController bean2 = applicationContext.getBean(DeptController.class);
System.out.println(bean2);
//根据bean的名称 及 类型获取
DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
System.out.println(bean3);
}
bean作用域
第三方bean
@Bean
- 如果要管理的bean对象来自第三方(不是自定义的),是无法用@Component及衍生注解声明bean的,就需要用到@Bean注解
- 若要管理的第三方bean对象,建议对这些bean进行集中分类配置,可以通过@Configuration注解声明一个配置类
java
@Configuration //配置类
public class CommonConfig {
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
//通过@Bean注解的name/value属性指定bean名称, 如果未指定, 默认是方法名
public SAXReader reader(DeptService deptService){
System.out.println(deptService);
return new SAXReader();
}
}
9.SpringBoot原理
- 起步依赖:依赖传递
- 自动配置
Springboot的自动配置就是当spring容器启动后,一些配置类、bean对象IU自动存入到了IOC容器,不需要我们手动去声明,简化了开发
自动配置原理
@Conditional注解
- 作用:按照一定的条件进行判断,在满足条件后才会注册对应的bean对象到Spring IOC容器中
- 位置:方法、类
- @Conditional本身是一个父注解,派生出大量的子注解
- @ConditionalOnClass:判断环境中是否有对应字节码文件,才注册bean到IOC容器