1、利用AspectJ实现一个日志记录插件
a、自定义注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ParamLog {
}
b、定义切面并添加记录逻辑
@Slf4j
@Aspect
@Order(1)
@Component
public class ParamLogAspect {
private final Gson gson = new GsonBuilder().create();
private ThreadLocal<LinkedList<Stopwatch>> stopWatchThreadLocal = new ThreadLocal<>();
@Pointcut("@annotation(com.jdt.megrez.common.log.ParamLog)|| @within(com.jdt.megrez.common.log.ParamLog)")
public void aopPoint() {
}
@Before("aopPoint()")
public void writeLog(JoinPoint joinPoint) {
if (log.isInfoEnabled()) {
try {
/* 栈 先进后出(解决嵌套计时问题)*/
LinkedList<Stopwatch> stopwatchLinkedList = stopWatchThreadLocal.get();
if (stopwatchLinkedList == null) {
stopwatchLinkedList = new LinkedList<>();
stopWatchThreadLocal.set(stopwatchLinkedList);
}
stopwatchLinkedList.push(Stopwatch.createStarted());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String className = signature.getDeclaringType().getSimpleName();
String methodName = signature.getName();
Object[] argsArr = joinPoint.getArgs();
//如果参数含有:java.io.FileInputStream, gson.toJson(args) 会栈溢出(对象存在嵌套问题:java.io.FileDescriptor.attach)
// (非序列化对象不打印)
StringBuilder builder = new StringBuilder();
for (Object args :argsArr) {
if(args instanceof Serializable){
builder.append(gson.toJson(args));
}else {
builder.append(args);
}
builder.append(" ");
}
log.info("[operation log] {}.{} , Params:{}", className, methodName, builder.toString());
} catch (Exception e) {
log.error("记录操作日志失败!!!!", e);
}
}
}
@AfterReturning(value = "aopPoint()", returning = "object")
public void afterReturning(JoinPoint joinPoint, Object object) {
if (log.isInfoEnabled()) {
try {
Stopwatch stopwatch = stopWatchThreadLocal.get().pop().stop();
Signature signature = joinPoint.getSignature();
String className = signature.getDeclaringType().getSimpleName();
String methodName = signature.getName();
log.info("[operation log returning] {}.{} , resp: {} , exec time {} ms", className, methodName, new Gson().toJson(object), stopwatch.elapsed(TimeUnit.MILLISECONDS));
} catch (Exception e) {
log.error("记录操作日志失败!!!!", e);
} finally {
if (stopWatchThreadLocal.get() != null && stopWatchThreadLocal.get().isEmpty()) {
stopWatchThreadLocal.remove();
}
}
}
}
@AfterThrowing(value = "aopPoint()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
if (log.isInfoEnabled()) {
try {
Stopwatch stopwatch = stopWatchThreadLocal.get().pop().stop();
Signature signature = joinPoint.getSignature();
String className = signature.getDeclaringType().getSimpleName();
String methodName = signature.getName();
log.error("[operation log throwing] {}.{} , exception:{}, exec time {} ms ", className, methodName, ex, stopwatch.elapsed(TimeUnit.MILLISECONDS));
} catch (Exception e) {
log.error("[operation log] 记录操作日志失败!!!!", e);
} finally {
if (stopWatchThreadLocal.get() != null && stopWatchThreadLocal.get().isEmpty()) {
stopWatchThreadLocal.remove();
}
}
}
}
}
2、Spring bean 对象获取工具
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static <T> T getBean(Class<T> cla) {
return applicationContext.getBean(cla);
}
public static <T> T getBean(String name, Class<T> cal) {
return applicationContext.getBean(name, cal);
}
public static String getProperty(String key) {
return applicationContext.getBean(Environment.class).getProperty(key);
}
/**
* 直接给applicationContext赋值,无法识别线程是否安全。所以增加synchronized方式赋值
*
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
setSyncApplicationContext(applicationContext);
}
private synchronized static void setSyncApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
}
3、跨域的一些配置
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
/*是否允许请求带有验证信息*/
corsConfiguration.setAllowCredentials(true);
/*允许访问的客户端域名*/
corsConfiguration.addAllowedOrigin("*");
/*允许服务端访问的客户端请求头*/
corsConfiguration.addAllowedHeader("*");
/*允许访问的方法名,GET POST等*/
corsConfiguration.addAllowedMethod("*");
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
4、日期工具类
1、计算日期间隔天数
a、使用 localdate 的 toEpochDay 方法 LocalDate.now().toEpochDay() - LocalDate.of(date.getYear(),date.getMonth(),date.toInstant()); b、使用 Period 类进行计算 Period.between(LocalDate.of(date.getYear(),date.getMonth(),date.getDate()), LocalDate.now());
5、mysql 分页动态参数
mybatis xml 中 imit (pageNum-1)*pageSize,pageSize,无法直接执行运算符语句,解决方式如下:
a、使用< bind />标签
<bind name="key_offset" value="(pageNum-1)*pageSize"></bind>
select * from table limit #{key_offset},#{pageSize}
b、注入
LIMIT ${(pageNo-1)*pageSize},${pageSize};
b、手动计算好,直接传值