记录了一些SpringBoot项目实战过程中知识点和常用思路

项目实战

全局异常捕获

  1. 创建全局异常类
  2. 通过@RestControllerAdvice注解将该类作用于全部controller类
  3. 在该类创建方法编写异常处理逻辑,行参为需要捕获的异常类
  4. 通过@ExceptionHandler注解捕获异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.sky.handler;

import com.sky.constant.MessageConstant;
import com.sky.exception.BaseException;
import com.sky.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.sql.SQLIntegrityConstraintViolationException;

/**
* 全局异常处理器,处理项目中抛出的业务异常
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

/**
* 捕获业务异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(BaseException ex){
log.error("异常信息:{}", ex.getMessage());
return Result.error(ex.getMessage());
}

/**
* 用于捕获sql异常
* @param ex
* @return
*/
@ExceptionHandler
public Result doSQLException(SQLIntegrityConstraintViolationException ex){
log.error("异常信息, {}", ex.getMessage());
String message = ex.getMessage();
if (message.contains("Duplicate")){
return Result.error(message.split(" ")[2] + MessageConstant.ALREADY_EXIST);
}
return Result.error(MessageConstant.UNKNOWN_ERROR);
}

}
注解 说明
@RestControllerAdvice 所有被@RestController注解的类/方法执行时都会触发该类
@ExceptionHandler 被注解方法能够捕获异常
@InitBinder 用来设置WebDataBinder,用于自动绑定前台请求参数到Model中(当表单数据需要转化时使用)
@ModelAttribute 本来作用是绑定键值对到Model中,当与@RestControllerAdvice配合使用时,可以让全局的@RequestMapping都能获得在此处设置的键值对。

ThreadLocal

​ ThreadLocal可以创建一个同一线程共享空间,一次请求在不同的层次为统一线程,所以该空间可以在拦截器,controller,service,mapper个层级之间共享。

方法名 说明
public void set(T value) 设置当线程的线程局部变量的值
public T get() 返回当前线程所对应的线程局部变量的值
public void remove() 移除当前线程的线程局部变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.sky.context;

public class BaseContext {

public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

public static void setCurrentId(Long id) {
threadLocal.set(id);
}

public static Long getCurrentId() {
return threadLocal.get();
}

public static void removeCurrentId() {
threadLocal.remove();
}

}

PageHelper 分页插件

1
2
3
4
5
6
7
8
public PageResult page(EmployeePageQueryDTO dto) {
// 1. 设置分页参数:当前页码和每页数据量
PageHelper.startPage(dto.getPage(),dto.getPageSize());

// 2. 调用Mapper查询方法
Page<Employee> page = employeeMapper.list(dto.getName());
return new PageResult(page.getTotal(), page.getResult());
}

日期格式化问题

​ 由于配置类WebMvcConfigurationSupport存在序列化问题,当配置类继承该类时会导致日期格式以数组形式返回

解决方法:

1. 在属性上加入注解
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Employee {

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;

private Long createUser;

private Long updateUser;

}
  1. 在配置类中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理(推荐)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器");
// 1. 创建消息转换器对象
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
// 2. 设置自定义的消息对象转换器
converter.setObjectMapper(new JacksonObjectMapper());
// 3. 将自定义的消息对象转换器设置为优先级最高
converters.add(0, converter);
}

package com.sky.json;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {

public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
//public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}

实体类拷贝

1
2
3
4
5
6
7
8
9
@Override
public void update(EmployeeDTO employeedto) {
Employee employee1 = new Employee();
//将employeedto内容拷贝到employee1
BeanUtils.copyProperties(employeedto, employee1);
employee1.setUpdateTime(LocalDateTime.now());
employee1.setUpdateUser(BaseContext.getCurrentId());
employeeMapper.update(employee1);
}

公共字段填充