该项目是使用了SpringBoot框架,整合了SSM框架的内容,基于注解开发的一个小项目。目的在于复习SpringBoot的相关知识。
一、Spring
1、Service层
DeptServiceImpl接口
public interface DeptService {
// 查询部门所有信息(不需要分页)
List<Dept> queryDept();
// 根据ID删除部门信息
void deleteDeptById(Integer id);
// 添加部门信息
void insertDept(Dept dept);
// 根据ID查询部门信息
Dept queryDeptById(Integer id);
// 修改部门信息
void modifyDept(Dept dept);
}
DeptServiceImpl实现类
@Service
public class DeptServiceImpl implements DeptService {
// 依赖注入
@Autowired
private DeptMapper deptMapper;
@Autowired
private EmpMapper empMapper;
// 查询部门所有信息(不需要分页)
@Override
public List<Dept> queryDept() {
return deptMapper.queryDept();
}
// 根据ID删除部门信息(需要开启事务)
@Override
@Transactional(rollbackFor = Exception.class) //开启事务,回归类型为全部异常
public void deleteDeptById(Integer id) {
// 删除部门
deptMapper.deleteDeptById(id);
// 删除该部门员工数据
empMapper.deleteEmpByDeptId(id);
}
// 添加部门信息
@Override
public void insertDept(Dept dept) {
dept.setCreateTime(LocalDateTime.now());
dept.setUpdateTime(LocalDateTime.now());
deptMapper.insertDept(dept);
}
// 根据ID查询部门信息
@Override
public Dept queryDeptById(Integer id) {
return deptMapper.queryDeptById(id);
}
// 修改部门信息
@Override
public void modifyDept(Dept dept) {
dept.setUpdateTime(LocalDateTime.now());
deptMapper.modifyDept(dept);
}
}
EmpService接口
public interface EmpService {
// 根据分页插件PageHelper分页查询 + 动态SQL
PageBean queryEmp(Integer page,
Integer pageSizeString,
String name,
Short gender,
LocalDate begin,
LocalDate end);
// 批量删除员工数据
void deleteEmp(List<Integer> ids);
// 添加员工数据
void insertEmp(Emp emp);
// 根据ID查询员工数据
Emp queryEmpById(Integer id);
// 修改员工数据
void updateEmp(Emp emp);
// 登录请求
Emp login(Emp emp);
}
EmpService实现类
@Service
public class EmpServiceImpl implements EmpService {
//依赖注入
@Autowired
private EmpMapper empMapper;
// 基本的分页查询
/*
@Override
public PageBean queryEmp(Integer page, Integer pageSize, String name, Short gender,
LocalDate begin, LocalDate end) {
Integer start = (page - 1) * pageSize;
// 查询
Long count = empMapper.count();
List<Emp> emps = empMapper.queryEmp(start, pageSize,name, gender, begin, end);
return new PageBean(count, emps);
}*/
// 根据分页插件PageHelper分页查询 + 动态SQL
@Override
public PageBean queryEmp(Integer page,
Integer pageSize,
String name,
Short gender,
LocalDate begin,
LocalDate end) {
// 1、设置分页参数,
PageHelper.startPage(page, pageSize);
// 2、执行查询
List<Emp> empList = empMapper.queryEmp(name, gender, begin, end);
Page<Emp> p = (Page<Emp>) empList;
// 3、封装PageBean对象
return new PageBean(p.getTotal(), p.getResult());
}
// 批量删除员工数据
@Override
public void deleteEmp(List<Integer> ids) {
empMapper.deleteEmp(ids);
}
// 添加员工数据
@Override
public void insertEmp(Emp emp) {
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
empMapper.insertEmp(emp);
}
// 根据ID查询员工数据
@Override
public Emp queryEmpById(Integer id) {
return empMapper.queryEmpById(id);
}
// 修改员工数据
@Override
public void updateEmp(Emp emp) {
emp.setUpdateTime(LocalDateTime.now());
empMapper.updateEmp(emp);
}
// 登录请求
@Override
public Emp login(Emp emp) {
return empMapper.getByUsernameAndPassword(emp);
}
}
2、IOC
IOC(Inversion of Control,控制反转) 是Spring框架的核心概念之一,它是一种设计原则,用于解耦组件之间的依赖关系。
控制反转(Inversion of Control): 控制反转是指将对象的创建和管理交给容器来负责,而不是由对象自己来负责创建和管理其依赖的对象。传统的程序流程是由程序员控制的,而在IOC容器中,控制权被颠倒了,由容器来控制对象之间的关系。
IOC容器(IOC Container): IOC容器是Spring框架的核心组件之一,它负责管理应用程序中的组件(或称为Bean)。在Spring中,Bean是由IOC容器创建、配置和管理的对象。
IOC容器的优点:
- 降低了组件之间的耦合度,使得组件之间的关系更加灵活和可维护。
- 提高了代码的可测试性,因为依赖关系被解耦,可以更容易地进行单元测试和集成测试。
- 提高了代码的可扩展性,因为新的组件可以通过配置的方式注入到应用程序中,而不需要修改现有的代码。
3、DI
依赖注入(Dependency Injection): 依赖注入是IOC的一种实现方式,它通过将依赖对象注入到目标对象中,实现了对象之间的解耦。在Spring中,依赖注入可以通过构造函数注入、Setter方法注入或字段注入等方式实现。
4、AOP
AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,用于将横切关注点(cross-cutting concerns)从应用程序的核心业务逻辑中分离出来,以提高代码的模块化性、可维护性和重用性。
Log自定义注解
@Retention(RetentionPolicy.RUNTIME) // 运行时生效
@Target(ElementType.METHOD) // 注解加在方法上
public @interface Log {
}
LogAspect切面类
@Slf4j
@Aspect // 切面类
@Component
public class LogAspect {
@Autowired
private OperateLogMapper operateLogMapper;
@Autowired
private HttpServletRequest httpServletRequest;
@Around("@annotation(com.itwxh.annotation.Log)") // 切入点(Pointcut)
public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
// 获得操作人ID(获得请求头令牌)
String jwt = httpServletRequest.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);
// 目标方法(Around才有目标方法)
long begin = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
// 获得方法返回值
String returnValue = JSONObject.toJSONString(result);
// 获得操作耗时
long costTime = end - begin;
// 记录操作日志
OperateLog operateLog = new OperateLog(null, operateUser, operateTime, className, methodName, methodParams, returnValue, costTime);
operateLogMapper.insert(operateLog);
return result;
}
}
5、事务管理
事务管理是数据库操作中的重要概念,用于确保数据操作的原子性、一致性、隔离性和持久性。在一个事务中,一组数据库操作要么全部成功执行,要么全部失败回滚,从而保持数据库的完整性和一致性。
// 根据ID删除部门信息(需要开启事务)
@Override
@Transactional(rollbackFor = Exception.class) //开启事务,回归类型为全部异常
public void deleteDeptById(Integer id) {
// 删除部门
deptMapper.deleteDeptById(id);
// 删除该部门员工数据
empMapper.deleteEmpByDeptId(id);
}
以上是基于 Spring 注解的声明式事务管理方式,通过 @Transactional 注解可以简单地标注在方法或类上,告诉 Spring 框架需要对该方法或类进行事务管理。
@Transactional 注解标注在方法或类上,并指定了 rollbackFor 属性为 Exception.class,表示当方法中抛出任何 Exception 类型的异常时,事务都应该回滚。
二、Spring MVC
Spring MVC 是 Spring Framework 中用于构建 Web 应用程序的一部分,它提供了一种基于 MVC(Model-View-Controller)设计模式的轻量级框架。通过 Spring MVC,开发者可以更容易地构建灵活、可扩展的 Web 应用程序。
MVC 设计模式:
Model(模型): 负责处理应用程序的业务逻辑和数据操作,通常代表着应用程序中的领域模型或实体对象。
View(视图): 负责将模型数据呈现给用户,通常是 HTML 页面或其他类型的用户界面。
Controller(控制器): 负责处理用户的请求,调用适当的业务逻辑处理程序并将处理结果返回给视图,通常是通过 URL 映射来决定哪个控制器处理特定的请求。
1、Controller层
DeptController
@RestController
@Slf4j
@RequestMapping("/depts") //公共路径
public class DeptController {
/*private static Logger log = LoggerFactory.getLogger(DeptController.class); !!! @Slf4j自动生成log*/
// 依赖注入
@Autowired
private DeptService deptService;
// 查询部门所有信息(不需要分页)
@GetMapping
public Result queryDept() {
log.info("查询全部部门的数据");
List<Dept> deptList = deptService.queryDept();
return Result.success(deptList);
}
// 根据ID删除部门信息
@Log
@DeleteMapping("/{id}")
public Result deleteDeptById(@PathVariable Integer id) {
log.info("删除部门的数据:{}", id);
deptService.deleteDeptById(id);
return Result.success();
}
// 添加部门信息
@Log
@PostMapping
public Result addDept(@RequestBody Dept dept) {
log.info("添加部门信息:{}", dept);
deptService.insertDept(dept);
return Result.success();
}
// 根据ID查询部门数据
@GetMapping("/{id}")
public Result queryDeptById(@PathVariable Integer id) {
log.info("根据id查询部门数据:{}", id);
// 注意返回类型是Dept
Dept deptList = deptService.queryDeptById(id);
return Result.success(deptList);
}
// 修改部门信息
@Log
@PutMapping
public Result modifyDept(@RequestBody Dept dept) {
log.info("修改部门信息");
deptService.modifyDept(dept);
return Result.success();
}
}
EmptController
@RestController
@Slf4j
@RequestMapping("/emps")
public class EmpController {
@Autowired
private EmpService empService;
// 根据分页插件PageHelper分页查询 + 动态SQL(按条件查询)
@GetMapping
public Result queryEmpByLimit(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
String name,
Short gender,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
log.info("分页查询员工数据:{},{},{},{},{},{}", page, pageSize, name, gender, begin, end);
PageBean pageBean = empService.queryEmp(page, pageSize, name, gender, begin, end);
return Result.success(pageBean);
}
// 批量删除员工数据
@Log
@DeleteMapping("/{ids}")
public Result deleteEmp(@PathVariable List<Integer> ids) {
log.info("批量删除操作, ids:{}", ids);
empService.deleteEmp(ids);
return Result.success();
}
// 添加员工数据
@Log
@PostMapping
public Result insertEmp(@RequestBody Emp emp) {
log.info("添加员工数据:{}", emp);
empService.insertEmp(emp);
return Result.success();
}
// 根据ID查询员工数据
@GetMapping("/{id}")
public Result queryEmpById(@PathVariable Integer id) {
log.info("根据ID查询员工数据:{}", id);
Emp emp = empService.queryEmpById(id);
return Result.success(emp);
}
// 修改员工数据(根据上面获得的ID修改)
@Log
@PutMapping
public Result updateEmp(@RequestBody Emp emp) {
log.info("修改员工数据:{}", emp);
empService.updateEmp(emp);
return Result.success();
}
}
2、拦截器 (interceptor)
拦截器(Interceptor) 是 Spring MVC 中的一种重要组件,用于在请求处理的不同阶段执行额外的逻辑。拦截器允许开发者在请求到达处理程序之前和之后执行一些预处理和后处理操作,例如身份验证、日志记录、性能监控等。拦截器与过滤器(Filter) 类似,但是它们更专注于针对请求处理的特定逻辑。
LoginCheckInterceptor
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.获取请求url。
//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
//3.获取请求头中的令牌(token)。
String jwt = request.getHeader("token");
log.info("从请求头中获取的令牌:{}", jwt);
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
if(!StringUtils.hasLength(jwt)) {
log.info("令牌不存在,返回未登录的信息");
Result error = Result.error("NOT_LOGIN");
//手动转换 对象--json --------> 阿里巴巴fastJSON
String jsonString = JSONObject.toJSONString(error);
response.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 --------> 阿里巴巴fastJSON
String jsonString = JSONObject.toJSONString(error);
response.getWriter().write(jsonString);
return false; // 不放行
}
//6.放行。
log.info("令牌合法, 放行");
return true;
}
}
要创建一个拦截器,只需实现 HandlerInterceptor 接口并重写其中的方法。然后,定义一个配置类WebConfig在配置类中注册拦截器,以便框架在适当的时候调用拦截器的方法。
WebConfig
@Configuration // @Configuration注解的作用:声明一个类为配置类,用于取代bean.xml配置文件注册bean对象。
public class WebConfig implements WebMvcConfigurer {
@Autowired
LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor)
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/login"); // 排除/login请求
}
}
3、全局异常处理
全局异常处理是一种在应用程序中集中处理异常的方法,它允许开发者在发生异常时执行统一的逻辑,而不必在每个可能发生异常的地方都进行处理。
@RestControllerAdvice // 定义了一个全局异常处理器
public class GlobalExceptionHandler {
// 处理异常
@ExceptionHandler(Exception.class)
public Result exception(Exception e) {
e.printStackTrace();
// 捕获到异常后,响应一个标准的Result
return Result.error("对不起,操作失败,请联系管理员!");
}
}
三、Mybatis
MyBatis 是一个开源的持久层框架,它是一个基于 Java 的持久化框架,用于将数据库操作与 Java 对象的映射关系进行配置化,简化了数据库操作过程,提高了开发效率。
- SQL语句和Java方法的解耦: MyBatis 使用 XML 文件或注解来将 SQL 语句与 Java方法进行映射,将 SQL 语句从Java 代码中解耦,使得 SQL 语句的维护更加方便。
- 参数绑定和结果映射: MyBatis 支持参数绑定,可以将 Java 对象作为参数传递给 SQL 语句,并将 SQL 查询结果映射到
Java 对象上。 - 动态 SQL: MyBatis 提供了强大的动态 SQL 功能,可以根据条件动态生成 SQL 查询语句,从而实现更加灵活的数据库操作。
- 缓存机制: MyBatis 支持一级缓存和二级缓存,一级缓存是 SqlSession 级别的缓存,二级缓存是 Mapper
级别的缓存,可以提高查询效率。 - 插件机制: MyBatis 提供了插件机制,可以在执行 SQL 语句的过程中插入自定义的拦截器,用于扩展 MyBatis 的功能。
1、Mapper层
DeptMapper
@Mapper
public interface DeptMapper {
// 查询部门所有信息(不需要分页)
@Select("select id, name, create_time, update_time from dept;")
List<Dept> queryDept();
// 根据ID删除部门数据
@Delete("delete from dept where id = #{id};")
void deleteDeptById(Integer id);
// 添加部门信息
@Insert("insert into dept (name, create_time, update_time) values (#{name},#{createTime},#{updateTime});")
void insertDept(Dept dept);
// 根据ID查询部门数据
@Select("select id, name, create_time, update_time from dept where id = #{id};")
Dept queryDeptById(Integer id);
// 修改部门信息
@Update("update dept set name = #{name}, update_time =#{updateTime} where id = #{id};")
void modifyDept(Dept dept);
}
EmpMapper
@Mapper
public interface EmpMapper {
// 以下是基本的分页查询
/*
// 查询总记录数
@Select("select count(*) from emp;")
Long count();
// 分页查询员工数据
@Select("select * from emp limit #{start},#{pageSize}")
// 问题:为什么要加@Param注解 !!!!!
List<Emp> queryEmp(@Param("start") Integer start,
@Param("pageSize") Integer pageSize,
@Param("name") String name,
@Param("gender") Short gender,
@Param("begin") LocalDate begin,
@Param("end") LocalDate end);
*/
// 根据分页插件PageHelper分页查询 + 动态SQL
// @Select("select * from emp") 在用分页插件是千万不要在sql语句后面加;号
List<Emp> queryEmp(@Param("name") String name,
@Param("gender") Short gender,
@Param("begin") LocalDate begin,
@Param("end") LocalDate end);
// 批量删除员工数据 (动态SQL)
void deleteEmp(@Param("ids") List<Integer> ids);
// 根据部门ID删除员工数据
@Delete("delete from emp where dept_id = #{id}")
void deleteEmpByDeptId(Integer 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})")
void insertEmp(Emp emp);
// 根据ID查询员工数据
@Select("select * from emp where id = #{id}")
Emp queryEmpById(Integer id);
// 修改员工数据 (动态SQL)
void updateEmp(@Param("emp") Emp emp);
// 根据用户名和密码查询员工数据
@Select("select * from emp where username = #{username} and password = #{password}")
Emp getByUsernameAndPassword(Emp emp);
}
EmpMapper.xml (动态SQL配置)
<?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.itwxh.mapper.EmpMapper">
<!--修改员工数据-->
<update id="updateEmp">
update emp
<set>
<if test="emp.username != null and emp.username != ''">
username = #{emp.username},
</if>
<if test="emp.name != null and emp.name != ''">
name = #{emp.name},
</if>
<if test="emp.gender != null">
gender = #{emp.gender},
</if>
<if test="emp.image != null and emp.image != ''">
image = #{emp.image},
</if>
<if test="emp.job != null">
job = #{emp.job},
</if>
<if test="emp.entrydate != null">
entrydate = #{emp.entrydate},
</if>
<if test="emp.deptId != null">
dept_id = #{emp.deptId},
</if>
<if test="emp.updateTime != null">
update_time = #{emp.updateTime}
</if>
</set>
where id = #{emp.id}
</update>
<!--批量删除员工-->
<delete id="deleteEmp">
delete
from emp
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
<!--分页条件查询员工-->
<select id="queryEmp" resultType="com.itwxh.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>
OperateLogMapper
@Mapper
public interface OperateLogMapper {
// 插入日志数据
@Insert("insert into operate_log " +
"(operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) VALUES " +
"(#{operateUser},#{operateTime},#{className},#{methodName},#{methodParams},#{returnValue},#{costTime})")
void insert(OperateLog log);
}
四、JavaWeb
1、过滤器
在JavaWeb中,过滤器(Filter) 是一种用于在请求到达Servlet之前或响应离开Servlet之后对请求进行预处理或后处理的组件。过滤器提供了一种方便的机制,用于对Web应用程序的请求进行过滤、修改或添加处理逻辑,而无需修改Servlet本身。它可以用于诸如日志记录、安全性检查、字符编码转换、数据压缩等方面。
LoginCheckFilter
@Slf4j
@WebFilter(urlPatterns = "/*") // 拦截所有请求
// 注意要在启动类加上注解 @ServletComponentScan !!!
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1.获取请求url。
String requestUrl = request.getRequestURL().toString();
log.info("请求URL为:{}", requestUrl);
//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
if (requestUrl.contains("/login")) {
log.info("登陆操作,放行");
filterChain.doFilter(servletRequest, servletResponse);
return;
}
//3.获取请求头中的令牌(token)。
String jwt = request.getHeader("token");
log.info("从请求头中获取的令牌:{}", jwt);
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
if (!StringUtils.hasLength(jwt)) {
log.info("令牌不存在,返回未登录的信息");
Result error = Result.error("NOT_LOGIN");
//手动转换 对象--json --------> 阿里巴巴fastJSON
String jsonString = JSONObject.toJSONString(error);
response.getWriter().write(jsonString);
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);
response.getWriter().write(notLogin);
return;
}
//6.放行。
log.info("令牌合法, 放行");
filterChain.doFilter(servletRequest, servletResponse);
}
}
2、Cookie、Session
Cookie和Session都是用于在Web开发中跟踪用户状态和信息的工具,但它们之间有一些关键的区别。
Cookie:
- Cookie是在客户端(通常是Web浏览器)保存的小型文本文件,由服务器发送到客户端,并保存在客户端的文件系统中。
- 每当客户端向服务器发送请求时,它都会将与该域相关联的Cookie信息一起发送。
- Cookie可以在客户端存储一些简单的数据,如用户首选项、登录信息等。
- Cookie有一些限制,比如大小限制(通常是4KB)和数量限制(不同浏览器有不同的限制)。
Session:
- Session是在服务器端维护的关于用户的状态信息。
- 每个会话都有一个唯一的标识符(通常是一个会话ID),该标识符通过Cookie或URL重写的方式发送到客户端,并在后续的请求中被服务器用来识别用户。
- 服务器使用会话来存储用户特定的数据,这些数据可以是任何类型的对象,而不仅仅是简单的键值对。
- 会话通常存储在服务器的内存中,但也可以被持久化到数据库或文件系统中。
SessionController
// Cookie、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);
}
}
五、解决方案工具
1、JWT令牌
JWT(JSON Web Token) 是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为 JSON 对象。它通常被用于身份验证和信息交换。JWT 由三部分组成,分别是头部(Header)、载荷(Payload)和签名(Signature)。
JWT 的工作流程通常如下:
- 用户进行身份认证后,服务端生成 JWT 并将其发送给客户端。
- 客户端将 JWT 存储在本地,并在将来的请求中将其发送到服务器。
- 服务器收到 JWT 后,验证其签名和有效期,并根据需要处理其中的信息。
LoginController
@Slf4j
@RestController
public class LoginController {
// 依赖注入
@Autowired
EmpService empService;
// 登录请求(JWT令牌会话跟踪)
@PostMapping("/login")
public Result login(@RequestBody Emp emp) {
log.info("登录操作: {}", emp);
Emp e = empService.login(emp);
// 如果查询到数据,则生成jwt令牌
if (e != null) {
HashMap<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);
} else {
return Result.error("用户名或者密码错误!");
}
}
}
JwtUtils 工具类
public class JwtUtils {
private static String signKey = "itwxh"; // 签名
private static Long expire = 43200000L; // 失效时间
//生成JWT令牌
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令牌
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
2、阿里云OSS存储
UploadController
@Slf4j
@RestController
public class UploadController {
@Autowired
AliOSSUtils aliOSSUtils;
@PostMapping("/upload")
public Result uploadFile(MultipartFile image) throws IOException {
log.info("文件上传:{}", image);
// 本地存储
/* // 获得原始文件名
String originalFilename = image.getOriginalFilename();
// 获得 UUID.JPG 随机码
String substring = null;
if (originalFilename != null) {
substring = originalFilename.substring(originalFilename.lastIndexOf("."));
}
String fileName = UUID.randomUUID() + substring;
log.info("新的文件名为:{}", fileName);
// 将文件存储在本地的磁盘目录中
image.transferTo(new File("D:\\Java\\project\\projectData\\" + fileName));
*/
// 阿里云存储
String url = aliOSSUtils.upload(image);
return Result.success(url);
}
}
AliOSSUtils (工具类)
// 阿里云 OSS 工具类
@Component
public class AliOSSUtils {
// 使用Value注解从配置文件中提取数据
/*
@Value("${aliyun.oss.endpoint}")
private String endpoint;
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret;
@Value("${aliyun.oss.bucketName}")
private String bucketName;*/
@Autowired
AliOSSProperties aliOSSProperties;
// 实现上传图片到OSS
public String upload(MultipartFile file) throws IOException {
// 使用ConfigurationProperties注解从配置文件中提取数据
String endpoint = aliOSSProperties.getEndpoint();
String accessKeySecret = aliOSSProperties.getAccessKeySecret();
String accessKeyId = aliOSSProperties.getAccessKeyId();
String bucketName = aliOSSProperties.getBucketName();
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();
// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
AliOSSProperties (实体类配置)
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
// 使用ConfigurationProperties注解从配置文件中提取数据
public class AliOSSProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
六、pojo实体类
1、Dept
@Data
@NoArgsConstructor
@AllArgsConstructor
// 部门实体类
public class Dept {
private Integer id;
private String name;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
2、Emp
@Data
@NoArgsConstructor
@AllArgsConstructor
// 员工实体类
public class Emp {
private Integer id;
private String username;
private String password;
private String name;
private Short gender;
private String image;
private Short job;
private LocalDate entrydate;
private Integer deptId;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
3、OperateLog
@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; //操作耗时
}
4、PageBean
@Data
@NoArgsConstructor
@AllArgsConstructor
// 分页插叙实体类
public class PageBean {
private Long total; //总记录数
private List rows; //数据列表
}
5、Result
@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);
}
}
七、配置文件
1、application.yml
# 应用服务 WEB 访问端口
server:
port: 8080
# MyBatis
mybatis:
# MyBatis映射
mapper-locations: classpath:mappers/*xml
type-aliases-package: com.itwxh.mybatis.entity
configuration:
#配置mybatis的日志, 指定输出到控制台
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#开启mybatis的驼峰命名自动映射开关 a_column ------> aCloumn
map-underscore-to-camel-case: true
spring:
#数据库连接信息
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/tlias
username: root
password: "030822"
#文件上传的配置
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
# 阿里云配置
aliyun:
oss:
endpoint: https://oss-cn-hangzhou.aliyuncs.com
accessKeyId: LTAI5tREtRVyp5yrjj5jHHBE
accessKeySecret: wNITIQNfV1g03C0lTf0tvlHqaaqGGd
bucketName: web-wxh-tlias
#spring事务管理日志
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
2、pom.xml
Spring Boot 项目的 pom.xml 文件通常会包含一些额外的配置,以便于集成 Spring Boot 相关的功能和插件。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>SpringBot-Web-tlias</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBot-Web-tlias</name>
<description>SpringBot-Web-tlias</description>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
<!--Web起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis的起步依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--mysql的起步依赖-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!--test的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- ======================= 以下是自己加上的依赖 ======================== -->
<!--lombok工具依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--PageHelper分页插件依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
<!--Spring Boot 中用于处理配置信息的处理器的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--fastJSON依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<!--AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--tlias-pojo的依赖-->
<dependency>
<groupId>com.itwxh</groupId>
<artifactId>tlias-pojo</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!--tlias-utils的依赖-->
<dependency>
<groupId>com.itwxh</groupId>
<artifactId>tlias-utils</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.itwxh.SpringBoWebTliasApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>