---黑马)
案例
开发规范Restful
开发流程
案例-部门管理-查询
controller层
package ikun.study.controller;
import ikun.study.pojo.Dept;
import ikun.study.pojo.Result;
import ikun.study.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 部门管理Controller
*/
@Slf4j // == private static Logger log = LoggerFactory.getLogger(DeptController.class);
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
/**<a href="http://demo.org:8080/depts">查询全部部门数据</a>
* 只允许get请求 其他请求方法报405*/
// @RequestMapping(value = "/depts",method = RequestMethod.GET)
@GetMapping("/depts")
public Result list() {
log.info("查询全部部门数据");
List<Dept> deptList = deptService.list();
return Result.success(deptList);
}
}
service层接口
package ikun.study.service;
import ikun.study.pojo.Dept;
import java.util.List;
/**
* 部门管理
*/
public interface DeptService {
/**
* 查询全部数据
* */
List<Dept> list();
}
service层实现类
package ikun.study.service.impl;
import ikun.study.mapper.DeptMapper;
import ikun.study.pojo.Dept;
import ikun.study.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Override
public List<Dept> list() {
return deptMapper.list();
}
}
mapper层
package ikun.study.mapper;
import ikun.study.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 部门管理
*/
@Mapper
public interface DeptMapper {
/**
* 查询全部部门数据
* */
@Select("select * from dept")
List<Dept> list();
}
删除部门-思路
controller层
/**
* 删除部门数据
*/
@DeleteMapping("/depts/{id}")
public Result delete(@PathVariable Integer id) {
log.info("根据id删除部门:{}",id);
//调用service删除部门
deptService.delete(id);
return Result.success();
}
service层接口
/**
* 删除部门
* */
void delete(Integer id);
}
service层实现类
@Override
public void delete(Integer id) {
deptMapper.deleteById(id);
}
mapper层
/**删除部门*/
@Delete("delete from dept where id=#{id}")
void deleteById(Integer id);
新增部门-思路
controller层
/**新增部门*/
@PostMapping("/depts")
public Result add(@RequestBody Dept dept) {
log.info("新增部门:{}",dept);
// 调用service新增部门
deptService.add(dept);
return Result.success();
}
service层接口
/**
* 新增部门
* */
void add(Dept dept);
service层实现类
@Override
public void add(Dept dept) {
dept.setCreateTime(LocalDateTime.now());
dept.setUpdateTime(LocalDateTime.now());
deptMapper.insert(dept);
}
mapper层
/**新增部门*/
@Insert("insert into dept(name, create_time, update_time) values (#{name},#{createTime},#{updateTime})")
void insert(Dept dept);
优化
案例-员工管理-分页查询
//仰晨study 创建时间2023/4/22 2:57 星期六
package ikun.study.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**分页查询结果封装类*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean {
private Long total;//总记录数
private List rows;//数据列表
}
分页插件Pagehelper
控制层
package ikun.study.controller;
import ikun.study.pojo.PageBean;
import ikun.study.pojo.Result;
import ikun.study.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 员工管理Controller
*/
@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {
@Autowired
private EmpService empService;
/**<a href="http://demo.org:8080/emps">分页查询</a>*/
@GetMapping
public Result parse(@RequestParam(defaultValue = "1") Integer page, //前端没给就设置默认值1 @RequestParam(defaultValue = "1")
@RequestParam(defaultValue = "10") Integer pageSize) {
log.info("分页查询,参数:{} {}",page,pageSize);
//调用service分页查询
PageBean pageBean = empService.page(page,pageSize);
return Result.success(pageBean);
}
}
服务层
package ikun.study.service;
import ikun.study.pojo.PageBean;
/**
* 员工管理
*/
public interface EmpService {
/**分页查询*/
PageBean page(Integer page, Integer pageSize);
}
package ikun.study.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import ikun.study.mapper.EmpMapper;
import ikun.study.pojo.Emp;
import ikun.study.pojo.PageBean;
import ikun.study.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EmpServiceImpl implements EmpService {
@Autowired
private EmpMapper empMapper;
@Override
public PageBean page(Integer page, Integer pageSize) {
// 1、获取总记录数
// 2、获取分页查询结果列表
// 3、封装在pageBean对象
// return new PageBean(empMapper.count(), empMapper.page((page-1)*pageSize,pageSize));
// 1、设置分页参数
PageHelper.startPage(page, pageSize);
// 2、执行查询
List<Emp> list = empMapper.list();
Page<Emp> p = (Page<Emp>) list;
// 3、封装pageBean对象
return new PageBean(p.getTotal(), p.getResult());
}
}
数据持久化层
package ikun.study.mapper;
import ikun.study.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 员工管理
*/
@Mapper
public interface EmpMapper {
// /**
// * 查询总记录数
// */
// @Select("select count(*) from emp")
// public Long count();
//
// /**
// * 分页查询,获取列表数据
// */
// @Select("select * from emp limit #{start},#{pageSize}")
// public List<Emp> page(Integer start, Integer pageSize);
/**员工信息查询 (统计和分页靠插件)*/
@Select("select * from emp")
public List<Emp> list();
}
条件分页查询
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="ikun.study.mapper.EmpMapper">
<select id="list" resultType="ikun.study.pojo.Emp">
select * from emp
<where>
<if test="name!=null and name!=''">
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>
</mapper>
案例-员工管理-删除员工
案例-员工管理-新增员工
@PostMapping
public Result save(@RequestBody Emp emp) {
log.info("新增员工:{}",emp);
empService.save(emp);
return Result.success();
}
/**新增员工*/
void save(Emp emp);
/**新增员工*/
@Override
public void save(Emp emp) {
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
empMapper.insert(emp);
}
/**新增员工*/
@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})")
void insert(Emp emp);
文件上传
@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:\Users\Dell\Desktop\
image.transferTo(new File("E:\\Users\\Dell\\Desktop\\"+newFileName));
return Result.success();
}
设置允许上传的文件大小
# 配置单个文件大小的限制 默认是1m
spring.servlet.multipart.max-file-size=10MB
# 配置多个文件(也叫单次请求)大小的限制 默认是10m
spring.servlet.multipart.max-request-size=100MB
阿里云(不知道怎么写了)
案例-员工管理-修改员工信息![](https://i-blog.csdnimg.cn/blog_migrate/afcd0e6fd8c56a46bb64250a8293f75a.png)
<!--更新员工信息-->
<update id="update">
UPDATE emp
<set>
<if test="username!=null and username!=''">username=#{username},</if>
<if test="password!=null and password!=''">password=#{password},</if>
<if test="name!=null and name!=''">name=#{name},</if>
<if test="gender!=null">gender=#{gender},</if>
<if test="image!=null and image!=''">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>
案例-配置文件-参数配置化
配置文件yml-yaml
server:
port: 9000
# 定义对象/Map集合
user:
name: ikun
age: 110
address: 蔡徐村
# 定义数组/List/set
hobby:
- java
- python
- C
application.properties转 application.yml
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/tlias
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=123456
#配置mybatis的日志, 指定输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#开启mybatis的驼峰命名自动映射开关 a_column ------> aCloumn
mybatis.configuration.map-underscore-to-camel-case=true
# 配置单个文件大小的限制 默认是1m
spring.servlet.multipart.max-file-size=10MB
# 配置多个文件(也叫单次请求)大小的限制 默认是10m
spring.servlet.multipart.max-request-size=100MB
# 阿里云OSS配置
aliyun.oss.endpoint=https://oss-cn-guangzhou.aliyuncs.com
aliyun.oss.accessKeyId=LTAI5tA7U94UYbRojy1Q9AQF
aliyun.oss.accessKeySecret=ZFNZN48828Lt0VIBEb2PvhsT2MRXia
aliyun.oss.bucketName=ikunikun
spring:
# 数据库的连接信息
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/tlias
username: root
password: 123456
# 文件上传配置
servlet:
multipart:
max-file-size: 10MB # 配置单个文件大小的限制 默认是1m
max-request-size: 100MB # 配置多个文件(也叫单次请求)大小的限制 默认是10m
# Mybatis配置
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #配置mybatis的日志, 指定输出到控制台
map-underscore-to-camel-case: true #开启mybatis的驼峰命名自动映射开关
# 阿里云配置
aliyun:
oss:
endpoint: https://oss-cn-guangzhou.aliyuncs.com
accessKeyId: LTAI5tA7U94UYbRojy1Q9AQF
accessKeySecret: ZFNZN48828Lt0VIBEb2PvhsT2MRXia
bucketName: ikunikun
登录接口
@PostMapping("/login")
public Result login(@RequestBody Emp emp) {
log.info("用户登录:{}",emp);
// emp = empService.login(emp);
// if (emp==null) return Result.error("账号或密码错误");
// return Result.success(emp);
return empService.login(emp)==null?Result.error("账号或密码错误"):Result.success();
}
/**用户登录*/
Emp login(Emp emp);
@Override
public Emp login(Emp emp) {
return empMapper.getByUsernameAndPassword(emp);
}
@Select("select * from emp where username = #{username} and password = #{password}")
Emp getByUsernameAndPassword(Emp emp);
登录效验
会话技术![](https://i-blog.csdnimg.cn/blog_migrate/3b43bd148600e81e58cee43f7f2f7313.png)
JWT令牌
/**生成JWT*/
@Test
public void testGenJwt() {
Map<String, Object> claims = new HashMap<>();
claims.put("id", 666);
claims.put("name", "Ikun");
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, "ikun") //签名算法+密钥
.setClaims(claims) //自定义内容(载荷)
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置有效时间时间为1小数
.compact();//变成字符串
System.out.println("jwt = " + jwt);
}
/**
* 解析JWT令牌
*/
@Test
public void testParseJwt() {
Claims claims = Jwts.parser()
.setSigningKey("ikun")
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSWt1biIsImlkIjo3NzcsImV4cCI6MTY4MjI2MTM1MH0=.vKu_uXjnOAt-_67nuCU22A2vt8P1KLo-DXM7cOHpJV4")
.getBody();
System.out.println(claims);
}
package ikun.study.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static String signKey = "ikun";
private static Long expire = 43200000L; //过期时间为12个小时
/**
* 生成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;
}
}
//仰晨study 创建时间2023/4/23 18:39 星期日
package ikun.study.controller;
import ikun.study.pojo.Emp;
import ikun.study.pojo.Result;
import ikun.study.service.EmpService;
import ikun.study.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
public class LoginController {
@Autowired
private EmpService empService;
@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); //jwt包涵当前登录员工信息
return Result.success(jwt);
}
//登录失败,返回错误信息
return Result.error("账号或密码错误");
}
}
过滤器(Filter)
快速入门
//仰晨study 创建时间2023/4/23 23:58 星期日
package ikun.study.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;//过滤器
import java.io.IOException;
@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![](https://i-blog.csdnimg.cn/blog_migrate/1673e2b7b05138ffe6bb7cc20e56ebe8.png)
//仰晨study 创建时间2023/4/24 1:00 星期一
package ikun.study.filter;
import com.alibaba.fastjson.JSONObject;
import ikun.study.pojo.Result;
import ikun.study.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@WebFilter("/*")
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();
log.info("请求的URL为:{}",url);
//2.判断请求头是否包含登录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)
ctrl+O==选择实现方法
//仰晨study 创建时间2023/4/24 1:45 星期一
package ikun.study.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override //目标资源方法运行前运行,返回true: 放行,放回false,不放行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle 目标资源方法运行前运行");
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 视图渲染完毕后运行,最后运行");
}
}
//仰晨study 创建时间2023/4/24 1:52 星期一
package ikun.study.config;
import ikun.study.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration //配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
/**
* 注册自己定义的拦截器LoginInterceptor
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
}
}
拦截器的执行流程
//仰晨study 创建时间2023/4/24 1:52 星期一
package ikun.study.config;
import ikun.study.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration //配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
/**
* 注册自己定义的拦截器LoginInterceptor
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor) //注册拦截器
.addPathPatterns("/**") //拦截全部路径 /*是一级路径/**是全部路径
.excludePathPatterns("/login"); //放行/login路径
}
}
//仰晨study 创建时间2023/4/24 1:45 星期一
package ikun.study.interceptor;
import com.alibaba.fastjson.JSONObject;
import ikun.study.pojo.Result;
import ikun.study.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override //目标资源方法运行前运行,返回true: 放行,放回false,不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
System.out.println("preHandle 目标资源方法运行前运行");
// //1.获取请求URL
// String url = req.getRequestURI();
// log.info("请求的URL为:{}",url);
//
// //2.判断请求头是否包含登录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);
resp.getWriter().write(notLogin);
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);
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 视图渲染完毕后运行,最后运行");
}
}
异常处理
//仰晨study 创建时间2023/4/24 20:41 星期一
package ikun.study.exception;
import ikun.study.pojo.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**全局异常处理器*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) //捕获所有异常
public Result ex(Exception exception) {
exception.printStackTrace();
return Result.error("ikun:对不起,操作失败,请联系管理员");
}
}
spring事务
# spring事务管理日志开关
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
/**
* Transactional:Spring事务管理(类,接口,方法都可以用(推荐,因为简单的增删改查都是不需要的))(下面的数据库方法 要么全部成功(自动提交)要么全部失败(自动回滚))
*/
@Transactional
@Override
public void delete(Integer id) {
deptMapper.deleteById(id); //根据ID删除部门
//int i = 1/0;
empMapper.deleteByDeptId(id); //根据部门id删除该部门下的员工信息
事务进阶
事务的回滚
1/0是运行是异常
/**
* Transactional:Spring事务管理(类,接口,方法都可以用(推荐,因为简单的增删改查都是不需要的))(下面的数据库方法 要么全部成功(自动提交)要么全部失败(自动回滚))
* <p>
* `@Transactional(rollbackFor)` 是 Spring 框架中用来配置事务回滚行为的注解,其中 `rollbackFor` 属性指定了哪些异常需要回滚事务。[(<a href="https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-depth">[1]</a>)][(<a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html">[2]</a>)][(<a href="https://www.jianshu.com/p/c5988db897fc">[3]</a>)][(<a href="https://www.baeldung.com/transaction-configuration-with-jpa-and-spring">[4]</a>)]
* 默认情况下,`@Transactional` 注解只会在遇到运行时异常或 Error 时回滚事务。但是对于一些非运行时异常,默认是不回滚事务的。而通过 `rollbackFor` 属性,我们可以指定一些特定的异常类型,使得其发生时事务也会回滚。需要注意的是,`rollbackFor` 属性默认值为空数组,表示不指定任何需要回滚的异常类型。如果设置为某个异常类型的 Class 对象,表示只有该异常发生时才会回滚事务;如果设置为多个异常类型的 Class 对象数组,表示这些异常发生时都会回滚事务。
*/
@Transactional(rollbackFor = Exception.class) //默认情况下只有运行时异常才会被回滚 rollbackFor指定所有异常都回滚
@Override
public void delete(Integer id) throws Exception {
deptMapper.deleteById(id); //根据ID删除部门
//int i = 1/0;
if (true) {
throw new Exception("ikun:错错错是我的错");
}
empMapper.deleteByDeptId(id); //根据部门id删除该部门下的员工信息
}
事务的传播行为
如果是默认值的话,那么虽然执行了finalli,但是因为一除零这个错误要回滚,所以他执行之后他也最终会回滚。所以必须要创建一个新的事务。
事务传播行为场景
AOP
<!--AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
//仰晨study 创建时间2023/4/25 3:20 星期二
package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect //AOP类
@Slf4j
public class TimeAspect {
@Around("execution(* com.itheima.service.*.*(..))") //切入点表达式
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
//1、记录开始时间
long begin = System.currentTimeMillis();
//2、调用原始方法运行
Object result = joinPoint.proceed();
//3、记录结束时间,计算方法耗时
long end = System.currentTimeMillis();
log.info(joinPoint.getSignature() + "方法耗时:{}ms",end-begin);
return result;
}
}
AOP基础核心概念
AOP进阶
//仰晨study 创建时间2023/4/25 3:20 星期二
package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect //AOP类
@Slf4j
public class MyAspect {
/**
* 把切入点表达式抽取出来
*/
@Pointcut("execution(* com.itheima.service.*.*(..))")
public void pt() {
}
@Before("pt()")
public void before() {
log.info("Before....");
}
@Around("pt()")
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Around before ...");
//2、调用原始方法运行
Object result = joinPoint.proceed();
log.info("Around after ...");
return result;
}
@After("pt()")
public void after() {
log.info("After....");
}
@AfterReturning("pt()")
public void afterReturning() {
log.info("AfterReturning....");
}
@AfterThrowing("pt()")
public void afterThrowing() {
log.info("AfterThrowing....");
}
}
AOP通知顺序![](https://i-blog.csdnimg.cn/blog_migrate/a2cb613c583c94603b870c8ee0124dcb.png)
下面给出一些常用的切入点表达式的 demo:
- 匹配所有 public 方法:
@Pointcut("execution(public * *(*))")
- 匹配所有名称以 "save" 开头的方法:
@Pointcut("execution(* save*(..))")
- 匹配 ProductService 接口上的所有方法:
@Pointcut("execution(* com.example.ProductService.*(..))")
- 匹配 Person 类上所有非 private 方法:
@Pointcut("execution(public * com.example.Person.*(..))")
- 匹配所有返回类型为 String 的方法:
@Pointcut("execution(public String *(..))")
需要注意的是,这些表达式只是常见的示例,实际应用中可能还需要根据具体业务场景进行定制化的表达式编写。同时,不同的 AOP 框架对于切入点表达式的支持也可能略有不同,具体情况可以参考框架文档进行了解。
切入点表达式
连接点
//仰晨study 创建时间2023/4/25 14:34 星期二
package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**学习连接点*/
@Aspect //切面类
@Slf4j
@Component
public class Demo2Aspect {
@Pointcut("execution(* com.itheima.service.DeptService.*(..))")
private void pt() {}
@Before("pt()")
public void before(JoinPoint joinPoint) {
log.info("Demo2Aspect...before...");
}
@Around("pt()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Demo2Aspect...Around-before...");
//1.获取目标对象的类名
String name = joinPoint.getTarget().getClass().getName();
log.info("目标对象的类名:{}", name);
//2.获取目标方法的方法名
String methodName = joinPoint.getSignature().getName();
log.info("目标对象的方法名:{}",methodName);
//3.获取目标方法运行时传入的参数
Object[] args = joinPoint.getArgs();
log.info("目标方法运行时传入的参数:{}", Arrays.toString(args));
//4.目标方法执行 无参就好 有参就吧获取到的参数在放回去就好了
Object result = joinPoint.proceed();
//5.获取目标方法运行的返回值
log.info("目标方法运行的返回值:{}",result);
log.info("Demo2Aspect...Around-after...");
return result;
}
}
案例
//仰晨study 创建时间2023/4/25 20:41 星期二
package ikun.study.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 用来面向切面的标记注解
*/
@Retention(RetentionPolicy.RUNTIME) //运行时有效
@Target(ElementType.METHOD) //允许写在方法上
public @interface Log {
}
//仰晨study 创建时间2023/4/25 20:45 星期二
package ikun.study.aop;
import com.alibaba.fastjson.JSONObject;
import ikun.study.mapper.OperateLogMapper;
import ikun.study.pojo.OperateLog;
import ikun.study.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;
@Slf4j
@Component
@Aspect //切面类
public class LogAspect {
@Autowired
private HttpServletRequest request;
@Autowired
private OperateLogMapper operateLogMapper;
@Around("@annotation(ikun.study.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 methodParams = Arrays.toString(args);
long begin = System.currentTimeMillis();
//调用原始方法
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
//方法返回值
String returnValue = JSONObject.toJSONString(result);
//操作时间
Long countTime = end - begin;
//记录操作日志
OperateLog operateLog = new OperateLog(null,operateUser,operateTime,className,methodName,methodParams,returnValue,countTime);
operateLogMapper.insert(operateLog);
log.info("AOP记录操作日志:{}",operateLog);
return result;
}
}