文章目录
- 🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)
- 🌟 亮点功能
- 📦 spring cloud模块概览
- 常用工具
- 🔗 更多信息
- 1.将sun-club-subject模块的登录拦截器放到sun-club-common包中
- 1.将context包和LoginUtil放到common包中,这样其他的子模块也可以获取loginId
- 2.修改一个bug,当不使用网关请求时,空指针异常 LoginInterceptor.java
- 2.sun-club-infra 编写MybatisInterceptor.java 对通用字段进行填充
- 1.MybatisInterceptor.java
- 2.测试
🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)
轻松高效的现代化开发体验
Sun Frame 是我个人开源的一款基于 SpringBoot 的轻量级框架,专为中小型企业设计。它提供了一种快速、简单且易于扩展的开发方式。
我们的开发文档记录了整个项目从0到1的任何细节,实属不易,请给我们一个Star!🌟
您的支持是我们持续改进的动力。
您的支持是我们持续改进的动力。
🌟 亮点功能
- 组件化开发:灵活选择,简化流程。
- 高性能:通过异步日志和 Redis 缓存提升性能。
- 易扩展:支持多种数据库和消息队列。
📦 spring cloud模块概览
- Nacos 服务:高效的服务注册与发现。
- Feign 远程调用:简化服务间通信。
- 强大网关:路由与限流。
常用工具
- 日志管理:异步处理与链路追踪。
- Redis 集成:支持分布式锁与缓存。
- Swagger 文档:便捷的 API 入口。
- 测试支持:SpringBoot-Test 集成。
- EasyCode:自定义EasyCode模板引擎,一键生成CRUD。
🔗 更多信息
- 开源地址:Gitee Sun Frame
- 详细文档:语雀文档
1.将sun-club-subject模块的登录拦截器放到sun-club-common包中
1.将context包和LoginUtil放到common包中,这样其他的子模块也可以获取loginId
2.修改一个bug,当不使用网关请求时,空指针异常 LoginInterceptor.java
2.sun-club-infra 编写MybatisInterceptor.java 对通用字段进行填充
1.MybatisInterceptor.java
package com.sunxiansheng.subject.infra.config;import com.sunxiansheng.subject.common.util.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.util.*;/*** Description: 填充created_by、created_time、update_by、update_time、is_deleted等公共字段的拦截器* @Author sun* @Create 2024/6/21 14:23* @Version 1.0*/
@Component
@Slf4j
// sql拦截器常规配置
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class
})})
public class MybatisInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];// 获取sql的类型SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();// 获取sql的参数对象Object parameter = invocation.getArgs()[1];// 如果参数对象为空,就直接放行if (parameter == null) {return invocation.proceed();}// 获取当前用户登录的idString loginId = LoginUtil.getLoginId();if (StringUtils.isBlank(loginId)) {return invocation.proceed();}// 如果sql的类型是插入或者更新,对参数做替换,填充created_by、created_time、update_by、update_time、is_deletedif (SqlCommandType.INSERT == sqlCommandType || SqlCommandType.UPDATE == sqlCommandType) {replaceEntityProperty(parameter, loginId, sqlCommandType);}// 放行return invocation.proceed();}// 填充created_by、created_time、update_by、update_time、is_deletedprivate void replaceEntityProperty(Object parameter, String loginId, SqlCommandType sqlCommandType) {if (parameter instanceof Map) {// Map类型,就相当于批量插入的那种情况replaceMap((Map) parameter, loginId, sqlCommandType);} else {// 普通的eneity类型replace(parameter, loginId, sqlCommandType);}}private void replaceMap(Map parameter, String loginId, SqlCommandType sqlCommandType) {// 获取到所有的参数对象Collection values = parameter.values();for (Object value : values) {// 将每个参数对象都进行替换replace(value, loginId, sqlCommandType);}}/*** 填充数据的逻辑* @param parameter* @param loginId* @param sqlCommandType*/private void replace(Object parameter, String loginId, SqlCommandType sqlCommandType) {// 如果是插入类型的sqlif (SqlCommandType.INSERT == sqlCommandType) {dealInsert(parameter, loginId);} else {// 由于只有插入类型和更新类型的sql类型会走到这里,所以这里一定就是更新类型了dealUpdate(parameter, loginId);}}/*** 更新类型的sql,填充数据的方式,对update_by、update_time进行填充* @param parameter* @param loginId*/private void dealUpdate(Object parameter, String loginId) {// 获取这个对象对应类的所有字段Field[] allFields = getAllFields(parameter);// 遍历字段for (Field field : allFields) {// 使用try-catch捕捉异常,使其不影响主程序的执行try {// 设置这个字段为可访问的field.setAccessible(true);// 得到字段的值,后面指定对象Object o = field.get(parameter);if (Objects.nonNull(o)) {// 如果字段的值不是空的,将这个字段设置成不可访问的field.setAccessible(false);// 然后跳过这次循环,遍历下一个字段continue;}// 如果字段的值是空的,则根据字段的名字来填充数据if ("updateBy".equals(field.getName())) {// 设置为用户的loginIdfield.set(parameter, loginId);// 设置字段为不可访问field.setAccessible(false);} else if ("updateTime".equals(field.getName())) {// 设置为当前时间field.set(parameter, new Date());// 设置字段为不可访问field.setAccessible(false);} else {// 如果不是这三个字段,就直接将这个字段设置为不可访问,然后遍历下一个字段field.setAccessible(false);}} catch (Exception e) {log.error("dealInsert.error:{}", e.getMessage(), e);}}}/*** 插入类型的sql,填充数据的方式,对created_by、created_time、is_deleted进行填充* @param parameter* @param loginId*/private void dealInsert(Object parameter, String loginId) {// 获取这个对象对应类的所有字段Field[] allFields = getAllFields(parameter);// 遍历字段for (Field field : allFields) {// 使用try-catch捕捉异常,使其不影响主程序的执行try {// 设置这个字段为可访问的field.setAccessible(true);// 得到字段的值,后面指定对象Object o = field.get(parameter);if (Objects.nonNull(o)) {// 如果字段的值不是空的,将这个字段设置成不可访问的field.setAccessible(false);// 然后跳过这次循环,遍历下一个字段continue;}// 如果字段的值是空的,则根据字段的名字来填充数据if ("isDeleted".equals(field.getName())) {// 默认设置为0,即未删除field.set(parameter, 0);// 设置字段为不可访问field.setAccessible(false);} else if ("createdBy".equals(field.getName())) {// 设置为用户的loginIdfield.set(parameter, loginId);// 设置字段为不可访问field.setAccessible(false);} else if ("createdTime".equals(field.getName())) {// 设置为当前时间field.set(parameter, new Date());// 设置字段为不可访问field.setAccessible(false);} else {// 如果不是这三个字段,就直接将这个字段设置为不可访问,然后遍历下一个字段field.setAccessible(false);}} catch (Exception e) {log.error("dealInsert.error:{}", e.getMessage(), e);}}}/*** 反射获取类的所有字段,并以字段数组的形式返回* @param object* @return*/private Field[] getAllFields(Object object) {// 获取当前对象对应类的Class对象Class<?> clazz = object.getClass();// 使用一个列表存储该类的所有字段信息List<Field> fieldList = new ArrayList<>();while (clazz != null) {// 将字段信息放到字段列表中fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));// clazz指向父类clazz = clazz.getSuperclass();}// 循环完成后,就会将当前的对象对应的类以及父类的字段对象放到字段列表中// 创建一个Field类型的数组,大小跟字段列表相同Field[] fields = new Field[fieldList.size()];// 将字段列表的元素放到数组中fieldList.toArray(fields);return fields;}@Overridepublic Object plugin(Object target) {// 返回一个代理对象,用于在方法调用时进行拦截处理return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 不需要任何处理}}