原型展示
准备工作
需求
环境搭建
一:创建数据库
-- 部门管理
create table dept(
id int unsigned primary key auto_increment comment '主键ID',
name varchar(10) not null unique comment '部门名称',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '部门表';
insert into dept (id, name, create_time, update_time) values(1,'学工部',now(),now()),(2,'教研部',now(),now()),(3,'咨询部',now(),now()), (4,'就业部',now(),now()),(5,'人事部',now(),now());
-- 员工管理(带约束)
create table emp (
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用户名',
password varchar(32) default '123456' comment '密码',
name varchar(10) not null comment '姓名',
gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',
image varchar(300) comment '图像',
job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',
entrydate date comment '入职时间',
dept_id int unsigned comment '部门ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '员工表';
INSERT INTO emp
(id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time) VALUES
(1,'jinyong','123456','金庸',1,'1.jpg',4,'2000-01-01',2,now(),now()),
(2,'zhangwuji','123456','张无忌',1,'2.jpg',2,'2015-01-01',2,now(),now()),
(3,'yangxiao','123456','杨逍',1,'3.jpg',2,'2008-05-01',2,now(),now()),
(4,'weiyixiao','123456','韦一笑',1,'4.jpg',2,'2007-01-01',2,now(),now()),
(5,'changyuchun','123456','常遇春',1,'5.jpg',2,'2012-12-05',2,now(),now()),
(6,'xiaozhao','123456','小昭',2,'6.jpg',3,'2013-09-05',1,now(),now()),
(7,'jixiaofu','123456','纪晓芙',2,'7.jpg',1,'2005-08-01',1,now(),now()),
(8,'zhouzhiruo','123456','周芷若',2,'8.jpg',1,'2014-11-09',1,now(),now()),
(9,'dingminjun','123456','丁敏君',2,'9.jpg',1,'2011-03-11',1,now(),now()),
(10,'zhaomin','123456','赵敏',2,'10.jpg',1,'2013-09-05',1,now(),now()),
(11,'luzhangke','123456','鹿杖客',1,'11.jpg',5,'2007-02-01',3,now(),now()),
(12,'hebiweng','123456','鹤笔翁',1,'12.jpg',5,'2008-08-18',3,now(),now()),
(13,'fangdongbai','123456','方东白',1,'13.jpg',5,'2012-11-01',3,now(),now()),
(14,'zhangsanfeng','123456','张三丰',1,'14.jpg',2,'2002-08-01',2,now(),now()),
(15,'yulianzhou','123456','俞莲舟',1,'15.jpg',2,'2011-05-01',2,now(),now()),
(16,'songyuanqiao','123456','宋远桥',1,'16.jpg',2,'2007-01-01',2,now(),now()),
(17,'chenyouliang','123456','陈友谅',1,'17.jpg',NULL,'2015-03-21',NULL,now(),now());
二:创建springboot工程
三:配置properties依赖
#驱动类名称
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=1234
#输出日志到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#开启驼峰自动映射
mybatis.configuration.map-underscore-to-camel-case=true
四:添加三层架构所需文件
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Result {
private Integer code;//响应码
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);
}
}
package com.itheima.controller;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DeptController {
}
package com.itheima.mapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DeptMapper {
}
package com.itheima.service.impl;
import com.itheima.service.DeptService;
import org.springframework.stereotype.Service;
@Service
public class DeptServiceImpl implements DeptService {
}
开发规范
开发流程
接口开发
一:部门管理
查询部门
@Autowired
private DeptService deptService;
@GetMapping("/depts")
public Result list(){
log.info("查询全部部门数据");
List<Dept> list=deptService.list();
return Result.success(list);
}
@Autowired
private DeptMapper deptMapper;
@Override
public List<Dept> list() {
return deptMapper.list();
}
@Mapper
public interface DeptMapper {
@Select("select * from dept")
List<Dept> list();
}
删除部门
/**
* 根据ID删除对应的部门
*/
@DeleteMapping("/depts/{id}")
public Result deleteById(@PathVariable Integer id){
log.info("根据id删除部门:{}",id);
deptService.deleteById(id);
return Result.success();
}
/**
* 根据id删除部门
*/
@Override
public void deleteById(Integer id) {
deptMapper.deleteById(id);
}
@Delete("delete from dept where id=#{id}")
void deleteById(Integer id);
新增部门
修改部门
/**
* 修改部门
* @param dept
* @return
*/
@PutMapping()
public Result modify(@RequestBody Dept dept){
log.info("修改部门:{}",dept.getName());
deptService.modifyDept(dept);
return Result.success();
}
/**
* 根据id查询部门
* @param id
* @return
*/
@GetMapping("/{id}")
public Result GetById(@PathVariable Integer id){
log.info("查询部门:{}",id);
Dept dept=deptService.GetById(id);
return Result.success(dept);
}
//service层
@Override
public void modifyDept(Dept dept) {
dept.setUpdateTime(LocalDateTime.now());
deptMapper.modifyDept(dept);
}
@Override
public Dept GetById(Integer id) {
Dept dept=deptMapper.GetById(id);
return dept;
}
//mapper层
@Update("update dept set name=#{name},update_time=#{updateTime} where id=#{id}")
void modifyDept(Dept dept);
@Select("select * from dept where id=#{id}")
Dept GetById(Integer id);
二:员工管理
分页查询
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 该类是封装要返回的数据总数和数据列表
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean {
private long totals;//总记录数
private List rows;//要返回的数据列表
}
controller层
/**
*
* @param page
* 接收前端的页码
* @param pageSize
* 接收前端的页面大小
* @return
*/
@GetMapping("/emps")
public Result page(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize){
PageBean pageBean=empService.page(page,pageSize);
return Result.success(pageBean);
}
service层
@Override
public PageBean page(Integer page, Integer pageSize) {
Integer start=(page-1)*pageSize;
List<Emp> list=empMapper.page(start,pageSize);
Long count=empMapper.count();
PageBean pageBean=new PageBean(count,list);
return pageBean;
}
mapper层
/**
* 查询并返回emp表中的数据数
* @return
*/
@Select("select count(*) from emp")
public long count();
/**
* 返回当前页的结果列表
* @param开始索引
* @parampageSize页面大小
* @return
*/
@Select("select * from emp limit #{start},#{pageSize}")
public List<Emp> page(Integer start,Integer pageSize);
使用pagehelper插件
<!--分页查询插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
service层
@Override
public PageBean page(Integer page, Integer pageSize) {
//1.设置分页参数
PageHelper.startPage(page,pageSize);
//2.执行查询
List<Emp> empList=empMapper.list();
Page<Emp> p=(Page<Emp>) empList;
//3.封装PageBean对象
PageBean pageBean=new PageBean(p.getTotal(),p.getResult());
return pageBean;
}
mapper层
/**
* 查询所有员工
* @return
*/
@Select("select * from emp")
List<Emp> list();
分页查询(带条件)
<?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">
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
<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>
</mapper>
controller层
/**
*
* @param page
* 接收前端的页码
* @param pageSize
* 接收前端的页面大小
* @return
*/
@GetMapping("/emps")
public Result page(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
Short gender,
String name,
@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate end
)
{
PageBean pageBean=empService.page(page,pageSize,gender,name,begin,end);
return Result.success(pageBean);
service层
@Override
public PageBean page(Integer page, Integer pageSize,
Short gender,
String name,
LocalDate begin,
LocalDate end
)
{
//1.设置分页参数
PageHelper.startPage(page,pageSize);
//2.执行查询
List<Emp> empList=empMapper.list(
gender,
name,
begin,
end
);
Page<Emp> p=(Page<Emp>) empList;
//3.封装PageBean对象
PageBean pageBean=new PageBean(p.getTotal(),p.getResult());
return pageBean;
}
mapper层
List<Emp> list( Short gender,
String name,
LocalDate begin,
LocalDate end);
删除员工
controller层
@DeleteMapping("/emps/{ids}")
public Result delete(@PathVariable List<Integer> ids){
empService.delete(ids);
return Result.success();
}
service层
@Override
public void delete(List<Integer> ids) {
empMapper.delete(ids);
}
mapper层
<delete id="delete">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
新增员工
controller层
@PostMapping("/emps")
public Result save(@RequestBody Emp emp){
empService.save(emp);
return Result.success();
}
service层
@Override
public void save(Emp emp) {
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
empMapper.save(emp);
}
mapper层
@Insert("insert into emp(username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time) " +
"values (#{username},#{password},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
void save(Emp emp);
文件上传
本地存储
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(MultipartFile image) throws IOException {
log.info("文件上传:{}",image);
//获取原始文件名
String originalFilename= image.getOriginalFilename();
//构建新的文件名
String newFilename= UUID.randomUUID().toString()+originalFilename.substring(originalFilename.lastIndexOf("."));
//将文件保存在服务端E:/images/目录下
image.transferTo(new File("D:/images/"+newFilename));
return Result.success();
}
}
若要修改上传文件大小,则要在properties文件下进行以下配置
#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB
MutipartFile类常用方法
阿里云oss
@Autowired
private AliOSSUtils aliOSSUtils;
@PostMapping("/upload")
public Result upload(MultipartFile image) throws IOException {
log.info("文件上传:{}",image.getOriginalFilename());
String url = aliOSSUtils.upload(image);
log.info("url:{}",url);
return Result.success(url);
}
查询回显
contr层
@GetMapping("/emps/{id}")
public Result getById(@PathVariable Integer id){
Emp emp=empService.getById(id);
return Result.success(emp);
}
service层
@Override
public Emp getById(Integer id) {
Emp emp=empMapper.getByID(id);
return emp;
}
mapper层
@Select("select * from emp where id=#{id}")
Emp getByID(Integer id);
修改员工
@PutMapping("/emps")
public Result update(@RequestBody Emp emp){
empService.update(emp);
return Result.success();
}
service层
@Override
public void update(Emp emp) {
emp.setUpdateTime(LocalDateTime.now());
empMapper.update(emp);
}
<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 and gender!=''">
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配置文件
#对象/map集合
user:
name: jack
age: 18
password: 123456
#数组/List/set集合
hobby:
-java
-game
-sport
configurationProperties
基础登录功能
登录
controller层
package com.itheima.controller;
import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
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;
@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);
return e!=null ? Result.success():Result.error("用户名或密码错误!");
}
}
service层
@Override
public Emp login(Emp emp) {
Emp e=empMapper.GetByPasswordAndUsername(emp);
return e;
}
mapper层
@Select("select * from emp where username=#{username} and password=#{password}")
Emp GetByPasswordAndUsername(Emp emp);
登录校验
会话技术
http session演示
package com.itheima;
import com.itheima.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* HttpSession演示
*/
@Slf4j
@RestController
public class SessionController {
//设置Cookie
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookie
return Result.success();
}
//获取Cookie
@GetMapping("/c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(cookie.getName().equals("login_username")){
System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie
}
}
return Result.success();
}
@GetMapping("/s1")
public Result session1(HttpSession session){
log.info("HttpSession-s1: {}", session.hashCode());
session.setAttribute("loginUser", "tom"); //往session中存储数据
return Result.success();
}
@GetMapping("/s2")
public Result session2(HttpServletRequest request){
HttpSession session = request.getSession();
log.info("HttpSession-s2: {}", session.hashCode());
Object loginUser = session.getAttribute("loginUser"); //从session中获取数据
log.info("loginUser: {}", loginUser);
return Result.success(loginUser);
}
}
浏览器第一次请求服务器,服务器设置cookie
客户端浏览器将cookie存储在本地
当客户端继续请求服务端时会携带cookie
会话跟踪方案对比
JWT令牌
payload 有效荷载采用base64编码,数字签名的加密算法采用Header里面声明的算法
jwt令牌生成和效验
@Test
public void genJwt(){
Map<String,Object> claims=new HashMap<>();
claims.put("id",1);
claims.put("name","jack");
String jwt = Jwts.builder()
.setClaims(claims)//设置自定义内容
.signWith(SignatureAlgorithm.HS256, "itheima")//设置加密算法和秘钥
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置有效期
.compact();
System.out.println(jwt);
}
@Test
public void parseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("itheima")//指定签名秘钥
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiamFjayIsImlkIjoxLCJleHAiOjE2OTMyMTUxODB9.2ZiD6WRVMjyjtAdAgAt4o5xaX5YWZoJ6izbOT1QU_Fg")
.getBody();
System.out.println(claims);
}
package com.itheima.controller;
import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import com.itheima.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);
return Result.success(jwt);
}
return Result.error("用户名或密码错误!");
}
}
token存储在了本地
过滤器Filter
因为Filter不属于spring boot 组件里面的,是属于java web 里面的三大组件之一,所以要加上ServletComponentScan注解在
@SpringBootApplication上
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")//拦截所有的请求
public class DemoFilter implements Filter{
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("此请求被拦截!");
}
}
过滤器链
执行流程
请求--》执行前逻辑--》放行--》资源--》执行后逻辑
package com.itheima.filter;
import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.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(urlPatterns = "/*")//拦截所有的请求
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//1.获取请求的URL
HttpServletRequest srq = (HttpServletRequest) servletRequest;
HttpServletResponse srp = (HttpServletResponse) servletResponse;
String url = srq.getRequestURL().toString();
log.info("请求的路径为:{}",url);
//2.判断请求的URL中是否包含login,如果包含,说明是登录操作,放行。
if(url.contains("login")){
log.info("登录操作,放行");
filterChain.doFilter(servletRequest,servletResponse);
return;
}
//3.获取请求头中的令牌(token)
String jwt = srq.getHeader("token");
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(!StringUtils.hasLength(jwt)){
log.info("令牌不存在,错误");
Result error = Result.error("NOT_LOGIN");
//手动转化为JSON格式的字符串返回给前端
String jsonString = JSONObject.toJSONString(error);
//响应头对象调取getwriter获取写入流响应回前端
srp.getWriter().write(jsonString);
return;
}
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT(jwt);
} catch (Exception e) {
e.printStackTrace();
log.info("令牌不存在,错误");
Result error = Result.error("NOT_LOGIN");
//手动转化为JSON格式的字符串返回给前端
String jsonString = JSONObject.toJSONString(error);
//响应头对象调取getwriter获取写入流响应回前端
srp.getWriter().write(jsonString);
return;
}
//6.放行
filterChain.doFilter(servletRequest,servletResponse);
}
}
登录效验案例
用postman测试
查询部门
解析成功放行
这里咱们把令牌故意改错
令牌解析错误
拦截器Interceptor
1.建立一个新包interceptor,然后添加一个新类,实现HanderInterceptor,按下快捷键Ctrl加O来重写方法
package com.itheima.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//加入IOC容器
public class LoginCheckInterceptor 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....");
}
}
2.在config包里面注册拦截器
package com.itheima.config;
import com.itheima.interceptor.LoginCheckInterceptor;
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 LoginCheckInterceptor loginCheckInterceptor;
@Override//注册拦截器
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
}
}
3.测试代码
拦截路径
过滤器和拦截器执行流程
登录校验
package com.itheima.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.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;
@Component//加入IOC容器
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override//目标资源方法运行前运行,返回true:放行,返回false:不放行
public boolean preHandle(HttpServletRequest srq, HttpServletResponse srp, Object handler) throws Exception {
//1.获取请求的URL
String url = srq.getRequestURL().toString();
log.info("请求的路径为:{}",url);
//2.判断请求的URL中是否包含login,如果包含,说明是登录操作,放行。
if(url.contains("login")){
log.info("登录操作,放行");
return true;
}
//3.获取请求头中的令牌(token)
String jwt = srq.getHeader("token");
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(!StringUtils.hasLength(jwt)){
log.info("令牌不存在,错误");
Result error = Result.error("NOT_LOGIN");
//手动转化为JSON格式的字符串返回给前端
String jsonString = JSONObject.toJSONString(error);
//响应头对象调取getwriter获取写入流响应回前端
srp.getWriter().write(jsonString);
return false;
}
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT(jwt);
} catch (Exception e) {
e.printStackTrace();
log.info("令牌不存在,错误");
Result error = Result.error("NOT_LOGIN");
//手动转化为JSON格式的字符串返回给前端
String jsonString = JSONObject.toJSONString(error);
//响应头对象调取getwriter获取写入流响应回前端
srp.getWriter().write(jsonString);
return false;
}
//6.放行
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....");
}
}
异常处理
package com.itheima.exception;
import com.itheima.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 ex){
ex.printStackTrace();
return Result.error("对不起,操作失败,请联系管理员");
}
}
事务
一旦出现异常就会出现事务的回滚
优化删除部门操作,删除部门的同时该部门下员工也要删除
/**
* 根据id删除部门
*/
@Transactional
@Override
public void deleteById(Integer id) {
deptMapper.deleteById(id);
empMapper.deleteDeptEmpById(id);
}
把代码改成这种,就会出现事务回滚,不会在数据库中删除
@Transactional
@Override
public void deleteById(Integer id) {
deptMapper.deleteById(id);
int i=1/0;
empMapper.deleteDeptEmpById(id);
}
事务进阶
rollbackFor
/**
* 根据id删除部门
*/
@Transactional(rollbackFor = Exception.class)//配置事务处理,默认是运行时异常才会处理,这里指定所有异常都会回滚
@Override
public void deleteById(Integer id) {
deptMapper.deleteById(id);
empMapper.deleteDeptEmpById(id);
}
propagation
一般会用前两个就行了
下面来看一个案例
/**
* 根据id删除部门
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)//配置事务处理,默认是运行时异常才会处理,这里指定所有异常都会回滚
@Override
public void deleteById(Integer id) {
try {
deptMapper.deleteById(id);
int i=1/0;
empMapper.deleteDeptEmpById(id);
} finally {
DeptLog deptLog=new DeptLog();
deptLog.setCreateTime(LocalDateTime.now());
deptLog.setDescription("执行了解散部门操作,此次解散的是"+id+"号部门");
deptLogService.insert(deptLog);
}
}
加上@Transactional(propagation = Propagation.REQUIRES_NEW)后会在下面这个方法中创建一个新事物,上面方法报错回滚不会影响下面
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void insert(DeptLog deptLog) {
deptLogMapper.insert(deptLog);
}
AOP基础
案例
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
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
@Slf4j
@Aspect//加入使用AOP
public class TimeAspect {
@Around("execution(* com.itheima.service.*.*(..))")
public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
//开始时间
long begin=System.currentTimeMillis();
//执行原始方法
Object object=proceedingJoinPoint.proceed();
long end=System.currentTimeMillis();
//获取方法结束时间,输出到日志
log.info(proceedingJoinPoint.getSignature()+"执行耗时:{}ms",end-begin);
return object;
}
}
Aop运用场景和优势
AOP核心概念
会先进入代理对象的方法,之后进入原来方法
通知类型
刷新查询部门,在调用接口方法前before和around执行
之后这些方法执行
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
@Slf4j
public class MyAspect {
//将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可。
@Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
public void pt(){}
/*
前置通知
*/
@Before("pt()")
public void before(){
log.info("before........");
}
/**
* 后置通知
*/
@After("pt()")
public void after(){
log.info("after........");
}
/**
* 环绕通知
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("pt()")
public Object before(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.info("Aroundbefore........");//最先通知
Object proceed = proceedingJoinPoint.proceed();
log.info("AroundAfter........");//最后通知
return proceed;
}
/**
* 返回后通知
*/
@AfterReturning("pt()")
public void AfterReturning(){
log.info("AfterReturning........");
}
}
通知顺序
切入点表达式
可以用||来匹配多个切入点
@annotation
@annotation(里面指定自定义注解路径)
连接点
代码
package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* 该类用于演示通知类型
*/
@Component
@Aspect
@Slf4j
public class MyAspect2 {
//将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可。
@Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
public void pt(){}
/*
前置通知
*/
@Before("pt()")
public void before(JoinPoint joinPoint){
log.info("Aspect2 before........");
}
/**
* 环绕通知
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("pt()")
public Object before(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.info("Aspect2 Around before........");//最先通知
//1.获取目标对象类名
String classname = proceedingJoinPoint.getTarget().getClass().getName();
log.info("目标对象类名为:{}",classname);
//2.获取目标对象方法名
String methodname = proceedingJoinPoint.getSignature().getName();
log.info("目标对象方法名为:{}",methodname);
//3.获取目标对象运行时传入的参数
Object [] args=proceedingJoinPoint.getArgs();
log.info("目标方法运行时传入的参数:{}", Arrays.toString(args));
//4.放行目标方法执行
Object result=proceedingJoinPoint.proceed();
//5.获取目标方法运行的返回值
log.info("目标方法运行的返回值:{}",result);
log.info("Aspect2 AroundAfter........");//最后通知
return result;
}
}
测试删除部门
//删除部门
@Test
public void AOPdeleteTest() {
deptService.delete(1);
}
AOP案例
引入AOP依赖
<!-- AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建数据库表和对应实体类
-- 操作日志表
create table operate_log(
id int unsigned primary key auto_increment comment 'ID',
operate_user int unsigned comment '操作人ID',
operate_time datetime comment '操作时间',
class_name varchar(100) comment '操作的类名',
method_name varchar(100) comment '操作的方法名',
method_params varchar(1000) comment '方法参数',
return_value varchar(2000) comment '返回值',
cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
private Integer id; //ID
private Integer operateUser; //操作人ID
private LocalDateTime operateTime; //操作时间
private String className; //操作类名
private String methodName; //操作方法名
private String methodParams; //操作方法参数
private String returnValue; //操作方法返回值
private Long costTime; //操作耗时
}
自定义注解log
package com.itheima.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 {
}
自定义切面类
package com.itheima.aop;
import com.alibaba.fastjson.JSONObject;
import com.itheima.mapper.OperateLogMapper;
import com.itheima.pojo.OperateLog;
import com.itheima.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.expression.spel.ast.OpMinus;
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 OperateLogMapper operateLogMapper;
@Autowired
private HttpServletRequest httpServletRequest;
@Around("@annotation(com.itheima.anno.Log)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
OperateLog operateLog=new OperateLog();
//获取操作人员ID,可以通过注入HttpServletRequest对象来获取token请求头中的jwt信息来获取,然后解析jwt令牌
String jwt = httpServletRequest.getHeader("token");
Claims claims = JwtUtils.parseJWT(jwt);
Integer operateUser=(Integer)claims.get("id");
operateLog.setOperateUser(operateUser);
//设置操作时间
LocalDateTime operateTime=LocalDateTime.now();
operateLog.setOperateTime(operateTime);
//设置操作类名
String className=joinPoint.getTarget().getClass().getName();
operateLog.setClassName(className);
//设置操作方法名
String methodName=joinPoint.getSignature().getName();
operateLog.setMethodName(methodName);
//设置操作方法参数
String methodParams= Arrays.toString(joinPoint.getArgs());
operateLog.setMethodParams(methodParams);
//设置操作方法返回值
Long begin=System.currentTimeMillis();
Object result = joinPoint.proceed();
//转化为json格式的字符串
String returnValue = JSONObject.toJSONString(result);
operateLog.setReturnValue(returnValue);
//设置操作时间
Long end=System.currentTimeMillis();
Long costTime=end-begin;
operateLog.setCostTime(costTime);
//插入数据
operateLogMapper.insertOperateLog(operateLog);
log.info("记录AOP日志:{}",operateLog);
return result;
}
}
测试:增加保卫部然后删除保卫部
日志
数据库
spring boot 项目配置
springboot支持三种配置文件方式
配置优先级
Bean管理
package com.itheima;
import com.itheima.controller.DeptController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
@SpringBootTest
public class BeanTest {
@Autowired
private ApplicationContext applicationContext;
@Test
void testGetBean(){
//根据name获取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
@Configuration
public class CommonConfig {
//声明第三方bean
@Bean//将第三方bean加入IOC容器
public SAXReader saxReader(){
return new SAXReader();
}
}