自动填充和自定义异常处理在Spring Boot中的应用
在现代的企业级应用中,我们经常需要自动填充一些公共字段(例如创建时间、更新时间等)和进行自定义的异常处理。本文将介绍如何在Spring Boot中实现这些功能。具体示例包括自动填充功能的实现、通过ThreadLocal保存当前用户信息、以及处理特定业务逻辑中的自定义异常。
自动填充公共字段
在企业应用中,数据库表通常包含创建时间(createTime)、更新时间(updateTime)等字段。我们可以通过自动填充功能,在插入和更新数据时自动设置这些字段,避免重复开发。下面是一个示例代码:
MetaObjectHandler实现类
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
Date now = new Date();
@Override
public void insertFill(MetaObject metaObject) {
log.info("进入自动插入方法");
metaObject.setValue("createTime", now);
metaObject.setValue("updateTime", now);
metaObject.setValue("createUser", BaseThreadLocal.getCurrentId());
metaObject.setValue("updateUser", BaseThreadLocal.getCurrentId());
}
@Override
public void updateFill(MetaObject metaObject) {
metaObject.setValue("updateTime", now);
metaObject.setValue("updateUser", BaseThreadLocal.getCurrentId());
}
}
使用ThreadLocal保存当前用户信息
为了在自动填充时获取当前用户的ID,我们可以使用ThreadLocal类。ThreadLocal为每个线程提供一个独立的变量副本,从而实现线程之间的隔离。
BaseThreadLocal类
package org.example.result;
public class BaseThreadLocal {
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
}
而这里的id可以从拦截器里进行拦截获取,拦截器代码实例:
package org.example.Filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.result.BaseThreadLocal;
import org.example.result.R;
import org.springframework.util.AntPathMatcher;
import javax.swing.plaf.PanelUI;
import java.io.IOException;
import com.alibaba.fastjson.JSON;
@WebFilter(filterName = "LoginFilter", urlPatterns = "/*")
public class LoginFilter implements Filter {
//路径匹配器
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest servletRequest1 = (HttpServletRequest) servletRequest;
HttpServletResponse servletResponse1 = (HttpServletResponse) servletResponse;
// 1获取本次请求的url
String requestURI = servletRequest1.getRequestURI();
//不需要拦截的路径
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
// 3. 匹配路径看是否是未登录
boolean check = check(urls, requestURI);
if (check) {
filterChain.doFilter(servletRequest1, servletResponse1);
return;
}
// 4.判断登录状态,如果已登录,则直接放行
if (servletRequest1.getSession().getAttribute("employee") !=null) {
Long empid= (Long) servletRequest1.getSession().getAttribute("employee");
//调用ThreadLocal工具类将id传过去************
BaseThreadLocal.setCurrentId(empid);
//**************
filterChain.doFilter(servletRequest1, servletResponse1);
return;
}
// 5.如果未登录则返回登录结果,前端会判断登录返回的信息去跳转登录页面
servletResponse1.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
return;
}
public boolean check(String[] urls, String requestURL) {
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURL);
if (match) {
return true;
}
}
return false;
}
}
控制层示例
以下是一个控制层示例,展示如何处理分类相关的请求,包括新增、分页查询、删除和更新操作。
CategoryController类
package org.example.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.example.pojo.Category;
import org.example.result.R;
import org.example.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/category")
@Slf4j
public class CategoryController {
@Autowired
private CategoryService categoryService;
@PostMapping
public R<String> save(@RequestBody Category category) {
categoryService.save(category);
return R.success("新增成功");
}
@GetMapping("/page")
public R<Page> page(int page, int pageSize) {
Page pageInfo = new Page(page, pageSize);
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByAsc(Category::getSort);
categoryService.page(pageInfo, queryWrapper);
return R.success(pageInfo);
}
@DeleteMapping
public R<String> delete(Long ids) {
categoryService.remove(ids);
return R.success("删除成功");
}
@PutMapping
public R<String> update(@RequestBody Category category) {
categoryService.updateById(category);
return R.success("修改成功");
}
}
自定义异常处理
在业务处理中,我们经常需要根据特定条件抛出自定义异常。例如,当分类关联了菜品或套餐时,禁止删除分类。
自定义异常类
package org.example.result;
public class CustonException extends RuntimeException {
public CustonException(String message) {
super(message);
}
}
CategoryService实现类
package org.example.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.pojo.Category;
import org.example.pojo.Dish;
import org.example.pojo.Setmeal;
import org.example.result.CustonException;
import org.example.service.CategoryService;
import org.example.mapper.CategoryMapper;
import org.example.service.DishService;
import org.example.service.SetmealService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
@Autowired
private DishService dishService;
@Autowired
private SetmealService setmealService;
@Override
public void remove(Long id) {
LambdaQueryWrapper<Dish> dishQueryWrapper = new LambdaQueryWrapper<>();
dishQueryWrapper.eq(Dish::getCategoryId, id);
int dishCount = dishService.count(dishQueryWrapper);
if (dishCount > 0) {
throw new CustonException("当前分类关联了菜品,不能删除");
}
LambdaQueryWrapper<Setmeal> setmealQueryWrapper = new LambdaQueryWrapper<>();
setmealQueryWrapper.eq(Setmeal::getCategoryId, id);
int setmealCount = setmealService.count(setmealQueryWrapper);
if (setmealCount > 0) {
throw new CustonException("当前分类关联了套餐,不能删除");
}
super.removeById(id);
}
}
总结
通过上述代码示例,我们展示了如何在Spring Boot应用中实现自动填充功能、使用ThreadLocal保存当前用户信息、以及自定义异常处理。这样的实现不仅提高了代码的可维护性和可读性,也增强了系统的健壮性和安全性。在实际项目中,可以根据业务需求灵活应用这些技术,提升开发效率。