项目总结

最近项目收获

1、Web消息格式

前言

​ 在Web项目开发中,我们都会和前端约定好,消息转换格式,便利于项目消息传输与使用,在此之前,我都是使用统一消息格式的类对象进行消息转换,而这次使用ResponseBodyAdvice接口,及逆行消息格式自动转换,并且自定义注解,在加上注解的类上或者是方法上,可以不进行消息类型的转换

ResponseBodyAdvice的使用

ResponseBodyAdvice接口和RequestBodyAdvice接口类似, RequestBodyAdvice是请求到Controller之前拦截,做相应的处理操作, 而ResponseBodyAdvice是对Controller返回的{@code @ResponseBody}or a {@code ResponseEntity} 后,{@code HttpMessageConverter} 类型转换之前拦截, 进行相应的处理操作后,再将结果返回给客户端.

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface ResponseBodyAdvice<T> {

/**
* 返回 true 则下面 beforeBodyWrite方法被调用, 否则就不调用下述方法
*/
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);

/**
* 消息具体处理方法
*/
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);

}

步骤

首先,制作统一返回消息格式

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
package comm.xss.coupon.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
* @author ==> 许帅帅
* @date 2023/8/6 10:00
* 功能描述: 通用返回消息格式
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResponse<T> implements Serializable {

private Integer code;
private String msg;
private T data;

public CommonResponse(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}

自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package comm.xss.coupon.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @author ==> 许帅帅
* @date 2023/8/6 10:04
* 功能描述: 忽略同意响应注解定义
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreResponseAdvice {
}

其次,实现ResponseBodyAdvice接口

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package comm.xss.coupon.advice;

import comm.xss.coupon.annotation.IgnoreResponseAdvice;
import comm.xss.coupon.vo.CommonResponse;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
* @author ==> 许帅帅
* @date 2023/8/6 10:14
* 功能描述: 消息转换器注解处理方法
*/
@RestControllerAdvice
@SuppressWarnings("all")
public class CommonResponseDataAdvice implements ResponseBodyAdvice<Object> {

/**
* @author ==> 许帅帅
* @Params: [methodParameter, aClass]
* @return: boolean
* @date 2023/8/6 10:15
* 功能描述: true 需要处理 false不需要处理
*/
@Override
public boolean supports(MethodParameter methodParameter,
Class<? extends HttpMessageConverter<?>> aClass) {

if (methodParameter.getDeclaringClass().isAnnotationPresent(
IgnoreResponseAdvice.class
)) {
return false;
}

if (methodParameter.getMethod().isAnnotationPresent(
IgnoreResponseAdvice.class
)) {
return false;
}

return true;
}


/**
* @author ==> 许帅帅
* @Params: [o, methodParameter, mediaType, aClass, serverHttpRequest, serverHttpResponse]
* @return: java.lang.Object
* @date 2023/8/6 10:16
* 功能描述: 消息具体处理方法
*/
@Override
public Object beforeBodyWrite(Object o,
MethodParameter methodParameter,
MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
CommonResponse<Object> response = new CommonResponse<>(
0, ""
);

if (o == null) {
return response;
} else if (o instanceof CommonResponse) {
response = (CommonResponse<Object>) o;
} else {
response.setData(o);
}


return response;
}
}

2、全局异常处理,在Web消息格式的基础上

1、自定义异常

1
2
3
4
5
6
7
8
9
10
11
package comm.xss.coupon.exception;

/**
* 通用异常处理
*/
public class CouponException extends Exception {

public CouponException(String exception) {
super(exception);
}
}

2、设置异常捕捉方法

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
package comm.xss.coupon.advice;

import comm.xss.coupon.exception.CouponException;
import comm.xss.coupon.vo.CommonResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

import static comm.xss.coupon.businessenum.ExceptionEnum.CouponExceptionEnum;

/**
* @author ==> 许帅帅
* @date 2023/8/6 10:59
* 功能描述: 全局异常处理
*/
@RestControllerAdvice
public class GlobalExceptionAdvice {

/**
* @author ==> 许帅帅
* @Params: [request, e]
* @return: comm.xss.coupon.vo.CommonResponse<java.lang.String>
* @date 2023/8/6 11:01
* 功能描述: 对CouponException进行处理
*/
@ExceptionHandler(CouponException.class)
public CommonResponse<String> handlerCouponException(
HttpServletRequest request,
CouponException e
) {
CommonResponse<String> commonResponse = new CommonResponse<>(
CouponExceptionEnum.getCode(), CouponExceptionEnum.getMsg()
);

commonResponse.setData(e.getMessage());

return commonResponse;
}
}

3、mybatis plus 在自定义序列化转换器

前言

​ 例如、在我们实体类中,想要从java程序序列化到数据库,或者从数据库序列化到java程序,有些字段类型是我们java中自定义的类型,然后序列化到数据库,我们需要进行类型的转化,或者从数据库序列化到java程序,但是类型不匹配,下面就需要我们使用类型转换器了。

1、加注解

  1. 在@TableName上面,开启自动化返回结果

  2. 加上**@TableFild(typehandler = "类型转换器")**

  3. 可以使用自己引入jar包的json转换器,例如fastjson的类型转换器,也可以自定义类型转换器

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
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "coupon_template", autoResultMap = true)
@JsonSerialize(using = CouponTemplateSerialize.class)
public class CouponTemplate implements Serializable {

@TableField(exist = false)
private static final long serialVersionUID = 1L;

/**
* 产品线
*/
@TableField(value = "product_line",typeHandler = ProductLineConverter.class)
private ProductLine productLine;

/**
* 目标用户
*/
@TableField(typeHandler = DistributeTargetConverter.class)
private DistributeTarget target;

/**
* 优惠券规则: TemplateRule 的 json 表示
*/
@TableField(value = "rule",typeHandler = FastjsonTypeHandler.class)
private TemplateRule rule;

}

2、自定义类型转换器

  1. 实现TypeHandler接口,实现里面的方法,进行转换

  2. 加注解、需要加上两个注解,一个是标注数据库需要转换的类型,二是java程序中的类型

    • @MappedTypes – 自己的java类型

    • @MappedJdbcTypes – 数据库类型,从JdbcType枚举类型中提取

      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
      ARRAY(2003),
      BIT(-7),
      TINYINT(-6),
      SMALLINT(5),
      INTEGER(4),
      BIGINT(-5),
      FLOAT(6),
      REAL(7),
      DOUBLE(8),
      NUMERIC(2),
      DECIMAL(3),
      CHAR(1),
      VARCHAR(12),
      LONGVARCHAR(-1),
      DATE(91),
      TIME(92),
      TIMESTAMP(93),
      BINARY(-2),
      VARBINARY(-3),
      LONGVARBINARY(-4),
      NULL(0),
      OTHER(1111),
      BLOB(2004),
      CLOB(2005),
      BOOLEAN(16),
      CURSOR(-10),
      UNDEFINED(-2147482648),
      NVARCHAR(-9),
      NCHAR(-15),
      NCLOB(2011),
      STRUCT(2002),
      JAVA_OBJECT(2000),
      DISTINCT(2001),
      REF(2006),
      DATALINK(70),
      ROWID(-8),
      LONGNVARCHAR(-16),
      SQLXML(2009),
      DATETIMEOFFSET(-155),
      TIME_WITH_TIMEZONE(2013),
      TIMESTAMP_WITH_TIMEZONE(2014);
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
package com.xss.coupon.converter;

import comm.xss.coupon.businessenum.CouponCategory;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
* @author ==> 许帅帅
* @date 2023/8/7 14:39
* 功能描述: CouponCategory 类型转换器
*/
@MappedTypes(CouponCategory.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class CouponCategoryConverter implements TypeHandler<CouponCategory> {

/**
* @author ==> 许帅帅
* @Params: [preparedStatement, i, couponCategory, jdbcType]
* @return: void
* @date 2023/8/7 14:46
* 功能描述: 向数据库中注入参数
*/
@Override
public void setParameter(PreparedStatement preparedStatement, int i, CouponCategory couponCategory, JdbcType jdbcType) throws SQLException {
/* 将CouponCategory 转换为 code 存入数据库 */
preparedStatement.setString(i, couponCategory.getCode());
}


/**
* @author ==> 许帅帅
* @Params: [resultSet, s]
* @return: comm.xss.coupon.businessenum.CouponCategory
* @date 2023/8/7 14:47
* 功能描述: 下面三个方法是数据库转到java程序中的方法
*/
@Override
public CouponCategory getResult(ResultSet resultSet, String s) throws SQLException {
return CouponCategory.of(resultSet.getString(s));
}

@Override
public CouponCategory getResult(ResultSet resultSet, int i) throws SQLException {
return CouponCategory.of(resultSet.getString(i));
}

@Override
public CouponCategory getResult(CallableStatement callableStatement, int i) throws SQLException {
return CouponCategory.of(callableStatement.getString(i));
}
}

4、SpringBoot整合线程池资源

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
62
63
64
65
66
67
68
69
70
71
package com.xss.coupon.config;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
* @author ==> 许帅帅
* @date 2023/8/7 19:54
* 功能描述: 自定义异步线程池
*/
@Slf4j
@EnableAsync
@Configuration
public class AsyncPoolConfig implements AsyncConfigurer {

@Bean
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(20);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("xss_coupon_pool");

executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);

executor.setRejectedExecutionHandler(
new ThreadPoolExecutor.CallerRunsPolicy()
);

executor.initialize();

return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}

@SuppressWarnings("all")
class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler{

@Override
public void handleUncaughtException(Throwable throwable,
Method method,
Object... objects) {
throwable.printStackTrace();
log.error("AnsyncError: {}, Method: {}, Param: {}",
throwable.getMessage(),
method.getName(),
JSON.toJSONString(objects));

// TODO 发送邮件短信,做进一步处理


}
}
}