本文将介绍SpringBoot项目常用的配置及工具类封装。
public class LogAspect {
private final static Logger LOG = LoggerFactory.getLogger(LogAspect.class);
/** 定义一个切点 */
@Pointcut("execution(public * com.example.*.controller..*Controller.*(..))")
public void controllerPointcut() {}
@Resource
private SnowFlake snowFlake;
@Before("controllerPointcut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 增加日志流水号
MDC.put("LOG_ID", String.valueOf(snowFlake.nextId()));
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Signature signature = joinPoint.getSignature();
String name = signature.getName();
// 打印请求信息
LOG.info("------------- 开始 -------------");
LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
LOG.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);
LOG.info("远程地址: {}", request.getRemoteAddr());
RequestContext.setRemoteAddr(getRemoteIp(request));
// 打印请求参数
Object[] args = joinPoint.getArgs();
// LOG.info("请求参数: {}", JSONObject.toJSONString(args));
Object[] arguments = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ServletRequest
|| args[i] instanceof ServletResponse
|| args[i] instanceof MultipartFile) {
continue;
}
arguments[i] = args[i];
}
// 排除字段,敏感字段或太长的字段不显示
String[] excludeProperties = {"password", "file"};
PropertyPreFilters filters = new PropertyPreFilters();
PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
excludefilter.addExcludes(excludeProperties);
LOG.info("请求参数: {}", JSONObject.toJSONString(arguments, excludefilter));
}
@Around("controllerPointcut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 排除字段,敏感字段或太长的字段不显示
String[] excludeProperties = {"password", "file"};
PropertyPreFilters filters = new PropertyPreFilters();
PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
excludefilter.addExcludes(excludeProperties);
LOG.info("返回结果: {}", JSONObject.toJSONString(result, excludefilter));
LOG.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
return result;
}
/**
* 使用nginx做反向代理,需要用该方法才能取到真实的远程IP
* @param request
* @return
*/
public String getRemoteIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
public enum ResultCodeEnum {
SUCCESS("200","成功"),
SUCCESS_VALIDATE("201","成功"),
SYSTEM_ERROR("500","系统异常");
public String code;
public String msg;
ResultCodeEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
}
@Data
public class MisException extends RuntimeException {
private String msg;
private int code = 500;
public MisException(Exception e) {
super(e);
this.msg = "执行异常";
}
public MisException(String msg) {
super(msg);
this.msg = msg;
}
public MisException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public MisException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public MisException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
}
@ControllerAdvice(basePackages={"com.example.mis.controller"})
public class ExceptionAdvice {
private static final Logger LOG = LoggerFactory.getLogger(ExceptionAdvice.class);
// 校验异常统一处理
@ExceptionHandler(BindException.class)
@ResponseBody
public R validExceptionHandler(BindException e) {
R result = new R();
LOG.warn("参数校验失败:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
result.setCode(ResultCodeEnum.SUCCESS_VALIDATE.code);
result.setData(false);
result.setMsg(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
return result;
}
//统一异常处理@ExceptionHandler,主要用于Exception
@ExceptionHandler(Exception.class)
@ResponseBody//返回json串
public R error(HttpServletRequest request, Exception e) throws MinioException {
long activityTimeout = StpUtil.getTokenActivityTimeout();
R result = new R();
if(activityTimeout < 0) {
LOG.error("异常错误:",e);
result.setCode(ResultCodeEnum.TOKEN_TIMEOUT.code);
result.setData(e.getMessage());
result.setMsg(e.getMessage());
return result;
}else {
LOG.error("异常错误:",e);
result.setCode(ResultCodeEnum.SYSTEM_ERROR.code);
result.setData(String.valueOf(e));
result.setMsg(ResultCodeEnum.SYSTEM_ERROR.msg);
return result;
}
}
}
@WebFilter(urlPatterns = "/*")
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
XssHttpServletRequestWrapper wrapper = new XssHttpServletRequestWrapper(request);
filterChain.doFilter(wrapper, servletResponse);
}
@Override
public void destroy() {
}
}
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value= super.getParameter(name);
if(!StrUtil.hasEmpty(value)){
value=HtmlUtil.cleanHtmlTag(value);
}
return value;
}
@Override
public String[] getParameterValues(String name) {
String[] values= super.getParameterValues(name);
if(values!=null){
for (int i=0;i<values.length;i++){
String value=values[i];
if(!StrUtil.hasEmpty(value)){
value=HtmlUtil.cleanHtmlTag(value);
}
values[i]=value;
}
}
return values;
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> parameters = super.getParameterMap();
LinkedHashMap<String, String[]> map=new LinkedHashMap();
if(parameters!=null){
for (String key:parameters.keySet()){
String[] values=parameters.get(key);
for (int i = 0; i < values.length; i++) {
String value = values[i];
if (!StrUtil.hasEmpty(value)) {
value = HtmlUtil.cleanHtmlTag(value);
}
values[i] = value;
}
map.put(key,values);
}
}
return map;
}
@Override
public String getHeader(String name) {
String value= super.getHeader(name);
if (!StrUtil.hasEmpty(value)) {
value = HtmlUtil.cleanHtmlTag(value);
}
return value;
}
@Override
public ServletInputStream getInputStream() throws IOException {
InputStream in= super.getInputStream();
InputStreamReader reader=new InputStreamReader(in, Charset.forName("UTF-8"));
BufferedReader buffer=new BufferedReader(reader);
StringBuffer body=new StringBuffer();
String line=buffer.readLine();
while(line!=null){
body.append(line);
line=buffer.readLine();
}
buffer.close();
reader.close();
in.close();
Map<String,Object> map=JSONUtil.parseObj(body.toString());
Map<String,Object> result=new LinkedHashMap<>();
for(String key:map.keySet()){
Object val=map.get(key);
if(val instanceof String){
if(!StrUtil.hasEmpty(val.toString())){
result.put(key,HtmlUtil.cleanHtmlTag(val.toString()));
}
}
else {
result.put(key,val);
}
}
String json=JSONUtil.toJsonStr(result);
ByteArrayInputStream bain=new ByteArrayInputStream(json.getBytes());
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bain.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH")
.allowedHeaders("*")
.maxAge(3600);
}
}
@Configuration
public class ThreadPoolConfig {
@Bean("AsyncTaskExecutor")
public AsyncTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(8);
// 设置最大线程数
executor.setMaxPoolSize(16);
// 设置队列容量
executor.setQueueCapacity(1000);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 线程名称的前缀
executor.setThreadNamePrefix("task-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//初始化线程池
executor.initialize();
return executor;
}
}
public class R {
private String code;
private String msg;
private Object data;
public R() {
}
private R(Object data) {
this.data = data;
}
public static R success() {
R r = new R();
r.setCode(ResultCodeEnum.SUCCESS.code);
r.setMsg(ResultCodeEnum.SUCCESS.msg);
return r;
}
public static R success(Object data) {
R r = new R(data);
r.setCode(ResultCodeEnum.SUCCESS.code);
r.setMsg(ResultCodeEnum.SUCCESS.msg);
return r;
}
public static R success(String code, String msg) {
R r = new R();
r.setCode(code);
r.setMsg(msg);
return r;
}
public static R error() {
R r = new R();
r.setCode(ResultCodeEnum.SYSTEM_ERROR.code);
r.setMsg(ResultCodeEnum.SYSTEM_ERROR.msg);
return r;
}
public static R error(String code, String msg) {
R r = new R();
r.setCode(code);
r.setMsg(msg);
return r;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
public class CopyUtil {
/**
* 单体复制
*/
public static <T> T copy(Object source, Class<T> clazz) {
if (source == null) {
return null;
}
T obj = null;
try {
obj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
BeanUtils.copyProperties(source, obj);
return obj;
}
/**
* 列表复制
*/
public static <T> List<T> copyList(List source, Class<T> clazz) {
List<T> target = new ArrayList<>();
if (!CollectionUtils.isEmpty(source)){
for (Object c: source) {
T obj = copy(c, clazz);
target.add(obj);
}
}
return target;
}
}
public class PageUtils {
@NotNull(message = "页码不能为空")
@Min(value = 1,message = "页码不能小于1")
private int pageNum = 1;
@NotNull(message = "每页条数不能为空")
@Max(value = 100,message = "每页条数不能超过100条")
private int pageSize = 10;
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
}
/**
* 密码生成工具类
* 功能:生成包含大小写字母、数字、特殊符号的安全密码
*/
public class PasswordUtils {
// 默认字符池定义
private static final String LOWER_CASE = "abcdefghijklmnopqrstuvwxyz";
private static final String UPPER_CASE = LOWER_CASE.toUpperCase();
private static final String NUMBERS = "0123456789";
private static final String SYMBOLS = "!@#$%^&*_";
/**
* 生成默认8位密码(包含大小写、数字、符号)
*/
public static String generate() {
return generate(8, true, true, true, true);
}
/**
* 自定义密码生成
* @param length 密码长度
* @param useLower 是否包含小写字母
* @param useUpper 是否包含大写字母
* @param useNumber 是否包含数字
* @param useSymbol 是否包含符号
*/
public static String generate(int length, boolean useLower, boolean useUpper,
boolean useNumber, boolean useSymbol) {
if (length < 4) {
throw new IllegalArgumentException("密码长度至少4位");
}
// 1. 构建字符池
StringBuilder charPool = new StringBuilder();
List<Character> mandatoryChars = new ArrayList<>();
if (useLower) {
charPool.append(LOWER_CASE);
mandatoryChars.add(RandomUtil.randomChar(LOWER_CASE));
}
if (useUpper) {
charPool.append(UPPER_CASE);
mandatoryChars.add(RandomUtil.randomChar(UPPER_CASE));
}
if (useNumber) {
charPool.append(NUMBERS);
mandatoryChars.add(RandomUtil.randomChar(NUMBERS));
}
if (useSymbol) {
charPool.append(SYMBOLS);
mandatoryChars.add(RandomUtil.randomChar(SYMBOLS));
}
if (charPool.length() == 0) {
throw new IllegalArgumentException("至少启用一种字符类型");
}
// 2. 确保每种类型至少一个字符
StringBuilder password = new StringBuilder();
for (Character ch : mandatoryChars) {
password.append(ch);
}
// 3. 填充剩余随机字符
int remaining = length - mandatoryChars.size();
for (int i = 0; i < remaining; i++) {
password.append(RandomUtil.randomChar(charPool.toString()));
}
// 4. 打乱顺序避免固定模式
List<Character> chars = new ArrayList<>();
for (char c : password.toString().toCharArray()) {
chars.add(c);
}
Collections.shuffle(chars);
StringBuilder finalPassword = new StringBuilder();
for (char c : chars) {
finalPassword.append(c);
}
return finalPassword.toString();
}
}
/**
* Twitter的分布式自增ID雪花算法
**/
@Component
public class SnowFlake {
/**
* 起始的时间戳
*/
private final static long START_STMP = 1609459200000L; // 2021-01-01 00:00:00
/**
* 每一部分占用的位数
*/
private final static long SEQUENCE_BIT = 12; //序列号占用的位数
private final static long MACHINE_BIT = 5; //机器标识占用的位数
private final static long DATACENTER_BIT = 5;//数据中心占用的位数
/**
* 每一部分的最大值
*/
private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
/**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId = 1; //数据中心
private long machineId = 1; //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳
public SnowFlake() {
}
public SnowFlake(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
/**
* 产生下一个ID
*
* @return
*/
public synchronized long nextId() {
long currStmp = getNewstmp();
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内,序列号置为0
sequence = 0L;
}
lastStmp = currStmp;
return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
| datacenterId << DATACENTER_LEFT //数据中心部分
| machineId << MACHINE_LEFT //机器标识部分
| sequence; //序列号部分
}
private long getNextMill() {
long mill = getNewstmp();
while (mill <= lastStmp) {
mill = getNewstmp();
}
return mill;
}
private long getNewstmp() {
return System.currentTimeMillis();
}
public static void main(String[] args) throws ParseException {
// 时间戳
// System.out.println(System.currentTimeMillis());
// System.out.println(new Date().getTime());
//
// String dateTime = "2021-01-01 08:00:00";
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// System.out.println(sdf.parse(dateTime).getTime());
SnowFlake snowFlake = new SnowFlake(1, 1);
long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
System.out.println(snowFlake.nextId());
System.out.println(System.currentTimeMillis() - start);
}
}
}
/**
* 树形结构转换工具类(支持泛型、循环依赖检测、排序等)
*/
public class TreeUtils {
/**
* 创建树构建器
* @param list 原始数据列表
* @param 节点类型
* @param ID类型
* @return 树构建器
*/
public static <T, K> TreeBuilder<T, K> build(List<T> list) {
return new TreeBuilder<>(list);
}
/**
* 树构建器(链式配置)
*/
public static class TreeBuilder<T, K> {
private final List<T> list;
private Function<T, K> idGetter;
private Function<T, K> parentIdGetter;
private Function<T, List<T>> childrenGetter;
private Comparator<T> comparator;
private K rootParentId;
private boolean checkCyclic = true;
public TreeBuilder(List<T> list) {
Objects.requireNonNull(list, "List cannot be null");
this.list = new ArrayList<>(list); // 避免原始列表被修改
}
/**
* 设置ID获取方法
*/
public TreeBuilder<T, K> id(Function<T, K> idGetter) {
this.idGetter = Objects.requireNonNull(idGetter, "idGetter cannot be null");
return this;
}
/**
* 设置父ID获取方法
*/
public TreeBuilder<T, K> parentId(Function<T, K> parentIdGetter) {
this.parentIdGetter = Objects.requireNonNull(parentIdGetter, "parentIdGetter cannot be null");
return this;
}
/**
* 设置子节点属性(通过Getter/Setter)
*/
public TreeBuilder<T, K> children(Function<T, List<T>> childrenGetter) {
this.childrenGetter = Objects.requireNonNull(childrenGetter, "childrenGetter cannot be null");
return this;
}
/**
* 设置排序规则
*/
public TreeBuilder<T, K> sorted(Comparator<T> comparator) {
this.comparator = comparator;
return this;
}
/**
* 设置根节点的父ID值(默认为null)
*/
public TreeBuilder<T, K> rootParentId(K rootParentId) {
this.rootParentId = rootParentId;
return this;
}
/**
* 是否检测循环依赖(默认开启)
*/
public TreeBuilder<T, K> checkCyclic(boolean checkCyclic) {
this.checkCyclic = checkCyclic;
return this;
}
/**
* 构建树形结构
*/
public List<T> build() {
validate();
Map<K, List<T>> parentIdMap = list.stream()
.collect(Collectors.groupingBy(parentIdGetter, Collectors.toList()));
Set<K> visitedIds = new HashSet<>(); // 用于循环依赖检测
return list.stream()
.filter(node -> {
K parentId = parentIdGetter.apply(node);
return Objects.equals(parentId, rootParentId) ||
(parentId == null && rootParentId == null);
})
.peek(root -> buildChildren(root, parentIdMap, visitedIds))
.sorted(comparator == null ? (a, b) -> 0 : comparator)
.collect(Collectors.toList());
}
private void validate() {
Objects.requireNonNull(idGetter, "idGetter must be set");
Objects.requireNonNull(parentIdGetter, "parentIdGetter must be set");
Objects.requireNonNull(childrenGetter, "childrenGetter must be set");
}
private void buildChildren(T node, Map<K, List<T>> parentIdMap, Set<K> visitedIds) {
K nodeId = idGetter.apply(node);
if (checkCyclic && !visitedIds.add(nodeId)) {
throw new IllegalStateException("检测到循环依赖,节点ID: " + nodeId);
}
List<T> children = parentIdMap.getOrDefault(nodeId, new ArrayList<>())
.stream()
.peek(child -> buildChildren(child, parentIdMap, visitedIds))
.sorted(comparator == null ? (a, b) -> 0 : comparator)
.collect(Collectors.toList());
if (!children.isEmpty()) {
childrenGetter.apply(node).clear();
childrenGetter.apply(node).addAll(children);
}
if (checkCyclic) {
visitedIds.remove(nodeId);
}
}
}
}