常见工具类

28 阅读5分钟

依赖注入(微服务下父模块和子模块)

多级菜单工具类生成树

自动引入自定义的类,通过调用里面的生成树方法,将需要进行生成树形数据的全体数据置于其中。

/**
 * 使用递归方法建菜单
 * @param sysMenuList
 * @return
 */
public static List<SysMenu> buildTree(List<SysMenu> sysMenuList) {
    List<SysMenu> trees = new ArrayList<>();
    for (SysMenu sysMenu : sysMenuList) {
        if (sysMenu.getParentId().longValue() == 0) {
            trees.add(findChildren(sysMenu,sysMenuList));
        }
    }
    return trees;
}
​
/**
 * 递归查找子节点
 * @param treeNodes
 * @return
 */
public static SysMenu findChildren(SysMenu sysMenu, List<SysMenu> treeNodes) {
    sysMenu.setChildren(new ArrayList<SysMenu>());
    for (SysMenu it : treeNodes) {
        if(sysMenu.getId().longValue() == it.getParentId().longValue()) {
            //if (sysMenu.getChildren() == null) {
            //    sysMenu.setChildren(new ArrayList<>());
            //}
            sysMenu.getChildren().add(findChildren(it,treeNodes));
        }
    }
    return sysMenu;
}
}

使用方法:直接调用方法,将需要转换为树形数据放到里面的方法即可

 List<SysMenu> sysMenuList =sysMenuMapper.selectAll();
        if (CollectionUtils.isEmpty(sysMenuList)) return null;
        List<SysMenu> treeList = MenuHelper.buildTree(sysMenuList); //构建树形数据

跨域请求(默认方案二)

将其置于工具类,主类的pom在引入工具类的模块。就可以对主类进行控制

方案一:在IndexController上添加 @CrossOrigin注解

@RestController
@RequestMapping(value = "/admin/system/index")
@CrossOrigin(allowCredentials = "true" , originPatterns = "*" , allowedHeaders = "*") 
public class IndexController {
​
}

弊端:每一个controller类上都来添加这样的一个接口影响开发效率、维护性较差

方案二:添加一个配置类配置跨域请求

// com.atguigu.spzx.manager.config
@Component
public class WebMvcConfiguration implements WebMvcConfigurer {
​
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")      // 添加路径规则
                .allowCredentials(true)               // 是否允许在跨域的情况下传递Cookie
                .allowedOriginPatterns("*")           // 允许请求来源的域规则
                .allowedMethods("*")
                .allowedHeaders("*") ;                // 允许所有的请求头
    }
    
}

拦截器

有些网页需要阻拦,有些不需要阻拦,可以放行

创建一个拦截器

@Component
public class LoginAuthInterceptor implements HandlerInterceptor {
​
    @Autowired
    private RedisTemplate<String , String> redisTemplate ;
​
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
​
        // 获取请求方式
        String method = request.getMethod();
        if("OPTIONS".equals(method)) {      // 如果是跨域预检请求,直接放行
            return true ;
        }
​
        // 获取token
        String token = request.getHeader("token");
        if(StrUtil.isEmpty(token)) {
            responseNoLoginInfo(response) ;
            return false ;
        }
​
        // 如果token不为空,那么此时验证token的合法性
        String sysUserInfoJson = redisTemplate.opsForValue().get("user:login:" + token);
        if(StrUtil.isEmpty(sysUserInfoJson)) {
            responseNoLoginInfo(response) ;
            return false ;
        }
​
        // 将用户数据存储到ThreadLocal中
        SysUser sysUser = JSON.parseObject(sysUserInfoJson, SysUser.class);
        AuthContextUtil.set(sysUser);
​
        // 重置Redis中的用户数据的有效时间
        redisTemplate.expire("user:login:" + token , 30 , TimeUnit.MINUTES) ;
​
        // 放行
        return true ;
    }
​
    //响应208状态码给前端
    private void responseNoLoginInfo(HttpServletResponse response) {
        Result<Object> result = Result.build(null, ResultCodeEnum.LOGIN_AUTH);
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JSON.toJSONString(result));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) writer.close();
        }
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AuthContextUtil.remove();  // 移除threadLocal中的数据
    }
}

拦截器注册

需要在WebMvcConfigurer进行实现注册

excludePathPatterns是排除不需要拦截的路径

addPathPatterns是需要拦截的路径,/**是对所有的路径进行拦截

public class WebMvcConfiguration implements WebMvcConfigurer {
    @Autowired
    private LoginAuthInterceptor loginAuthInterceptor;
    //拦截器注册
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(loginAuthInterceptor)
            .excludePathPatterns("/admin/system/index/login" , "/admin/system/index/generateValidateCode","/admin/**",                "/doc.html",
                    "/v3/api-docs/**",
                    "/webjars/**",
                    "/swagger-resources/**",
                    "/favicon.ico")//不需要拦截路径
            .addPathPatterns("/**");//所有路径拦截
}

全局异常处理器

定义自定义异常(将其封装好)

package com.atguigu.spzx.exception;
​
import com.atguigu.spzx.model.vo.common.ResultCodeEnum;
import lombok.Data;
​
@Data
public class GuiguException extends RuntimeException {
​
    private Integer code ;          // 错误状态码
    private String message ;        // 错误消息
​
    private ResultCodeEnum resultCodeEnum ;     // 封装错误状态码和错误消息
​
    public GuiguException(ResultCodeEnum resultCodeEnum) {
        this.resultCodeEnum = resultCodeEnum ;
        this.code = resultCodeEnum.getCode() ;
        this.message = resultCodeEnum.getMessage();
    }
​
    public GuiguException(Integer code , String message) {
        this.code = code ;
        this.message = message ;
    }
​
}

加入全局异常捕获器

捕获两种异常GuiguException与Exception,抛出哪种异常则调用哪种方法。

@ControllerAdvice
public class GlobalExceptionHandler {
​
    @ExceptionHandler(value = GuiguException.class)     // 处理自定义异常
    @ResponseBody
    public Result error(GuiguException exception) {
        exception.printStackTrace();//打印java异常栈调用
        return Result.build(null , exception.getResultCodeEnum()) ;
    }
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        return Result.build(null , 201,"出现了异常") ;
    }
}

UUID生成Key

String token = UUID.randomUUID().toString().replace("-", "");

随机数字验证码

String s = RandomUtil.randomNumbers(4);

生成图片验证码

 CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(150, 48, 4, 20);
        String codeValue = circleCaptcha.getCode();
        String imageBase64 = circleCaptcha.getImageBase64();
​
        // 生成uuid作为图片验证码的key
        String codeKey = UUID.randomUUID().toString().replace("-", "");

Md5加密

 String md5InputPassword = DigestUtils.md5DigestAsHex(inputPassword.getBytes());

Knife4jConfig

@Configuration
public class Knife4jConfig {
​
    @Bean
    public GroupedOpenApi adminApi() {      // 创建了一个api接口的分组
        return GroupedOpenApi.builder()
                .group("admin-api")         // 分组名称
                .pathsToMatch("/admin/**")  // 接口请求路径规则
                .build();
    }
​
    /***
     * @description 自定义接口信息
     */
    @Bean
    public OpenAPI customOpenAPI() {
​
        return new OpenAPI()
                .info(new Info()
                        .title("尚品甑选API接口文档")
                        .version("1.0")
                        .description("尚品甑选API接口文档")
                        .contact(new Contact().name("atguigu"))); // 设定作者
    }
​
}

上下文线程共享

如果登陆过需要获取角色信息,调用该方法即可

package com.atguigu.spzx.utils;
​
import com.atguigu.spzx.model.entity.system.SysUser;
​
public class AuthContextUtil {
    //创建threadlocal对象
    private static final ThreadLocal<SysUser> threadLocal = new ThreadLocal<>() ;
    // 定义存储数据的静态方法
    public static void set(SysUser sysUser) {
        threadLocal.set(sysUser);
    }
​
    // 定义获取数据的方法
    public static SysUser get() {
        return threadLocal.get() ;
    }
​
    // 删除数据的方法
    public static void remove() {
        threadLocal.remove();
    }
}

RedisConfig工具类

依赖需要加入

<!-- redis的起步依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

工具类代码

@Configuration  // 声明这是一个 Spring 配置类
public class RedisConfig {
​
    @Bean  // 将方法返回的对象注册为 Spring 容器中的 Bean
    public CacheManager cacheManager(LettuceConnectionFactory connectionFactory) {
        // 1. 定义序列化器(用于 Redis Key 和 Value 的序列化)
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
​
        // 2. 配置 Redis 缓存行为
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))  // 设置缓存默认过期时间:600 秒(10 分钟)
                // 指定 Key 的序列化方式(String 类型,避免 Redis 中出现二进制乱码)
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                // 指定 Value 的序列化方式(JSON 格式,支持存储复杂对象)
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer));
​
        // 3. 构建 RedisCacheManager
        RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)  // 应用上述配置
                .build();
​
        return cacheManager;
    }
}

公共类定义手机号格式,方便判断错误

package com.hmdp.utils;
​
import cn.hutool.core.util.StrUtil;
import com.hmdp.constants.RegexConstants;
​
/**
 * @author ghp
 */
public class RegexUtils {
    /**
     * 是否是无效手机格式
     * @param phone 要校验的手机号
     * @return true:符合,false:不符合
     */
    public static boolean isPhoneInvalid(String phone){
        return mismatch(phone, RegexConstants.PHONE_REGEX);
    }
    /**
     * 是否是无效邮箱格式
     * @param email 要校验的邮箱
     * @return true:符合,false:不符合
     */
    public static boolean isEmailInvalid(String email){
        return mismatch(email, RegexConstants.EMAIL_REGEX);
    }
​
    /**
     * 是否是无效验证码格式
     * @param code 要校验的验证码
     * @return true:符合,false:不符合
     */
    public static boolean isCodeInvalid(String code){
        return mismatch(code, RegexConstants.VERIFY_CODE_REGEX);
    }
​
    // 校验是否不符合正则格式
    private static boolean mismatch(String str, String regex){
        if (StrUtil.isBlank(str)) {
            return true;
        }
        return !str.matches(regex);
    }
}
​
public static boolean isPhoneInvalid(String phone){
    return mismatch(phone, RegexConstants.PHONE_REGEX);
}

//mismatch()方法接收两个数组,并返回两个数组之间第一个不同项的索引。 例如, {1, 2, 3, 4, 5}{1, 2, 3, 5, 8} 在索引 3 上不同。

正则表达式

public abstract class RegexConstants {
    /**
     * 手机号正则
     */
    public static final String PHONE_REGEX = "^1([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9])";
    /**
     * 邮箱正则
     */
    public static final String EMAIL_REGEX = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$";
    /**
     * 密码正则。4~32位的字母、数字、下划线
     */
    public static final String PASSWORD_REGEX = "^\w{4,32}$";
    /**
     * 验证码正则, 6位数字或字母
     */
    public static final String VERIFY_CODE_REGEX = "^[a-zA-Z\d]{6}$";
​
}

Result返回封装

package com.atiguigu.Result;
​
com.atiguigu.Result.Result
​
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
​
    private int code;
    private String message;
    private T data;
​
    public Result(T data) {
        this.code = 200;
        this.message = "success";
        this.data = data;
    }
​
    public Result(T data, boolean success, String message) {
        if (success) {
            this.code = 200;
            this.message = "success";
        } else {
            this.code = 500;
            this.message = message;
        }
        this.data = data;
    }
​
    public Result(int code, String message) {
        this.code = code;
        this.message = message;
        this.data = null;
    }
​
    public static <T> Result<T> success(T data) {
        return new Result<>(data);
    }
​
    public static <T> Result<T> fail(String message) {
        return new Result<>(500, message);
    }
​
    public static <T> Result<T> fail(int code, String message) {
        return new Result<>(code, message);
    }
}
​