SpringBoot应用常用的工具类

  • 一、统一返回结果类
  • 二、统一异常处理类
  • 1. 全局异常类
  • 2. 自定义异常类
  • 三、常用的配置类
  • 1. MybatisPlus配置类
  • 2. Redis配置类
  • 3. Swagger2配置类


一、统一返回结果类

在前后端分离系统中,统一返回结果类的作用更为突出,其主要作用包括以下几个方面:

a.格式统一:在前后端分离系统中,前端通过Ajax请求来获取后端数据,而每个请求都需要获取后端数据,并且数据格式需要保持一致。通过使用统一返回结果类,可以规范前后端数据传输格式和约定,减少数据解析和格式判断的开销。

b.错误处理:前后端分离系统中,错误处理非常重要,错误提示信息需要明确,便于开发人员识别和修复。统一返回结果类可以将错误信息封装在同一个结构体中,有利于前端进行错误提示和用户交互。

c.数据传输效率高:在前后端分离系统中,由于前端需要通过Ajax请求来向后台请求数据,每个请求都被视为一个独立的HTTP请求,可以使用同一个返回结果类降低请求的传输开销,以提高应用程序的效率和响应速度。

d.易于扩展:在前后端分离系统中,数据传输需要的格式可能随着需求变更而改变。通过使用统一返回结果类,可以方便地修改结构体,来适应新需求和扩展。

综合来看,利用统一返回结果类,可以规范前后端数据传输格式和约定,减少数据解析和错误处理的开销,提高数据传输效率和用户体验。

下面有一个结果类的模板:

@Data
public class Result<T> {

    // 状态码
    private Integer code;
    // 信息
    private String message;
    // 数据
    private T data;

    // 构造私有化
    private Result() { }

    // 设置数据,返回对象的方法
    public static<T> Result<T> build(T data,Integer code,String message) {
        // 创建Resullt对象,设置值,返回对象
        Result<T> result = new Result<>();
        // 判断返回结果中是否需要数据
        if(data != null) {
            // 设置数据到result对象
            result.setData(data);
        }
        // 设置其他值
        result.setCode(code);
        result.setMessage(message);
        // 返回设置值之后的对象
        return result;
    }

    // 设置数据,返回对象的方法
    public static<T> Result<T> build(T data,ResultCodeEnum resultCodeEnum) {
        // 创建Resullt对象,设置值,返回对象
        Result<T> result = new Result<>();
        // 判断返回结果中是否需要数据
        if(data != null) {
            // 设置数据到result对象
            result.setData(data);
        }
        //设置其他值
        result.setCode(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMessage());
        //返回设置值之后的对象
        return result;
    }

    // 成功的方法
    public static<T> Result<T> ok(T data) {
        Result<T> result = build(data, ResultCodeEnum.SUCCESS);
        return result;
    }

    // 失败的方法
    public static<T> Result<T> fail(T data) {
        return build(data,ResultCodeEnum.FAIL);
    }

}

统一返回结果状态信息类:

@Getter
public enum ResultCodeEnum {

    SUCCESS(200,"成功"),
    FAIL(201, "失败"),

    SERVICE_ERROR(2012, "服务异常"),
    DATA_ERROR(204, "数据异常"),
    ILLEGAL_REQUEST(205, "非法请求"),
    REPEAT_SUBMIT(206, "重复提交"),

    LOGIN_AUTH(208, "未登陆"),
    PERMISSION(209, "没有权限"),
    ...
    ;

    private Integer code;

    private String message;

    private ResultCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

用登录作为例子来使用此类:

@RestController
@RequestMapping("/admin")
public class AdminController {
    @Autowired
    private LoginService loginService;

    @PostMapping("login")
    public Result findRegionByKeyword() {
        Map<String,String> map = new HashMap<>();
        map.put("token","token-admin");
        return Result.ok(map);
    }
}

二、统一异常处理类

1. 全局异常类

在项目开发中,为了保证代码的健壮性,需要对抛出的异常进行处理,SpringBoot全局异常类通过AOP对所有程序中可能出现的异常进行捕获和统一处理,统一异常处理,方便维护和管理。系统中出现异常时,如果没有进行及时处理的话,容易导致系统崩溃,影响整个应用的运行,全局异常捕捉能让系统及时发现并解决异常问题,保证系统的健壮性。

// AOP 面向切面
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class) // 异常处理器
    @ResponseBody  // 返回json数据
    // 调用前面统一返回结果类
    public Result error(Exception e) {
        e.printStackTrace();
        return Result.fail(null);
    }

    // 自定义异常处理
    @ExceptionHandler(ZiDingYiException.class)
    @ResponseBody
    public Result error(ZiDingYiException exception) {
        return Result.build(null,exception.getCode(),exception.getMessage());
    }
}

2. 自定义异常类

自定义异常类可以根据业务需求自定义异常,将业务异常与其他类型的异常分开处理,允许开发人员自定义自己的异常类。

@Data
public class ZiDingYiException extends RuntimeException{

    // 异常状态码
    private Integer code;

    /**
     * 通过状态码和错误消息创建异常对象
     * @param message
     * @param code
     */
    public ZiDingYiException(String message, Integer code) {
        super(message);
        this.code = code;
    }
    
    /**
     * 接收枚举类型对象
     * @param resultCodeEnum
     */
    public ZiDingYiException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
    }
}

三、常用的配置类

配置类只需将代码复制到项目中即可使用

1. MybatisPlus配置类

@Configuration
@MapperScan("com.xxx.*.mapper")
@EnableTransactionManagement
public class MybatisPlusConfig {

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,
     * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
     * 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

2. Redis配置类

@Configuration
@EnableCaching
public class RedisConfig {

    // 使用默认标签做缓存
    @Bean
    public KeyGenerator wiselyKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    // 声明模板
    /*
    ref = 表示引用
    value = 具体的值
    <bean class="org.springframework.data.redis.core.RedisTemplate" >
        <property name="defaultSerializer" ref = "">
    </bean>
     */
    //  工具类:
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        //  将Redis 中 string ,hash 数据类型,自动序列化!
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(365))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

3. Swagger2配置类

@Configuration
@EnableSwagger2WebMvc
public class Swagger2Config {

    //1
    @Bean
    public Docket webApiConfig(){
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("userId")
                .description("用户token")
                //.defaultValue(JwtHelper.createToken(1L, "admin"))
                .defaultValue("1")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tokenPar.build());

        Docket webApi = new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                //只显示api路径下的页面
                .apis(RequestHandlerSelectors.basePackage("com.xxx.xxx")) // 扫描包路径
                .paths(PathSelectors.regex("/api/.*"))
                .build()
                .globalOperationParameters(pars);
        return webApi;
    }

    //2
    @Bean
    public Docket adminApiConfig(){
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("adminId")
                .description("用户token")
                .defaultValue("1")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tokenPar.build());

        Docket adminApi = new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                //只显示admin路径下的页面
                .apis(RequestHandlerSelectors.basePackage("com.xxx.xxx")) // 扫描包路径
                //.paths(PathSelectors.regex("/admin/.*"))
                .build()
                .globalOperationParameters(pars);
        return adminApi;
    }

    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("网站-API文档")
                .description("本文档描述...")
                .version("1.0")
                .contact(new Contact("name", "url", "email"))
                .build();
    }

    private ApiInfo adminApiInfo(){
        return new ApiInfoBuilder()
                .title("后台管理系统-API文档")
                .description("本文档描述...")
                .version("1.0")
                .contact(new Contact("name", "url", "email"))
                .build();
    }
}