一.异步记录日志
1.场景
有事务的情况下,记录将异常信息记录到异常表中
2.处理过程
2.1本来是加上never或者notsurport注解,然后在throw抛异常之前把异常信息插入数据库,但是未成功,抛异常时回滚
2.2finally中调用被@Async注解的方法
2.3注意 这个带@Async注解的方法要放在其他的类里边
调用代码
public Object logHandler(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = signature.getMethod();
String methodName = method.getName();
String methodDeclaringClass = method.getDeclaringClass().getSimpleName();
Object[] args = proceedingJoinPoint.getArgs();
String targetMethodParams= Arrays.toString(args);//TODO 入参取的不对
long startTime = System.currentTimeMillis();
log.info("调用方法:【{}.{}】 开始!入参是【{}】", methodDeclaringClass, methodName,targetMethodParams);
//异常记录表
Pf1013 pf1013 = new Pf1013();
try {
return proceedingJoinPoint.proceed();
} catch (Exception exception) {
log.error("exception,调用方法:【{}.{}】 入参是【{}】,操作人是【{}】",methodDeclaringClass, methodName,targetMethodParams,GlobalVarValueUtil.getOperCode());
pf1013.setErrorInfo(ExceptionUtils.getStackTrace(exception).substring(0,8000));
throw exception;
} finally {
//异步存表
flowAsyncHelper.savePlatExceptionLog(pf1013);
log.info("调用方法:【{}.{}】 结束,耗时:{}ms, 请求参数【{}】", methodDeclaringClass, methodName,System.currentTimeMillis()-startTime,targetMethodParams);
}
}
async注解代码
@Component
@Slf4j
public class AsyncHelper {
@Autowired
private Pf1013Mapper pf1013Mapper;
/**
* 记录异常日志
*
*/
@Async//(AsyncConfig.LOCAL_FLOW_EXECUTOR)
public void savePlatExceptionLog(Pf1013 pf1013) {
if(StringUtils.isNotEmpty(pf1013.getErrorInfo())){
pf1013Mapper.insert(pf1013);
}
}
}
奇怪的是,没有配置configuration也没有在启动类中@EnableAsync就好用了,眼睁睁看到抛出异常之后我的异常表里边就有数据了
续集来了
以上操作之后 本地确实是好用的 但是 线上确实是不好用的
所以 使用@async还是有注意事项的
1.异步方法和调用异步方法的方法不能再同一个类
2.异步方法加注解@Async,调用异步方法的方法所在类要加注解@EnableAsync
3.方法所属的类的对象需要是被Spring容器所管理的,也就是指被@Controller @Service @Repository @Component这些注解的类
修改方法 在上边第一段代码loghandler方法所在类上边开启异步
因为loghandler是调用@async方法的类
后续
另一个项目我也想异步记录日志,有一个参数我在异步之前的调用类可以获取到但是在async这个类里边获取不到,点进去看,原来这个参数存储在线程变量中,然而异步是单独开出来一个线程,所以获取不到
protected static ThreadLocalStack<Map<Class<?>, Object>> contextualData = new ThreadLocalStack();
二.获取异步处理结果
1.AsyncResult
异步想获得执行结果,需要将被@Async注解的方法改为Future返回,使用AsyncResult
/**
* 参数分类表发布到指定城市
* @param pb207
* @param cityId
*/
@DataSourceKey(spel = "#cityId")
@Async
public Future<Boolean> publishPb207(PB207 pb207, String cityId){
//发布之前查询指定城市数据库中的pb207中是否存在相应csflm数据,存在就不发布
PB207 pb207City = pb207Mapper.selectByPrimaryKey(pb207.getCsflm());
if(ObjectUtils.isEmpty(pb207City)){
pb207Mapper.insert(pb207);
return new AsyncResult<>(Boolean.valueOf(false));
}
return new AsyncResult<>(Boolean.valueOf(true));//指定城市是否存在参数分类数据
}
2.Future.get
异步阻塞
首先,同步异步,阻塞非阻塞,是两个概念
Future<Boolean> exits = publishUtil.publishPb207(pb207, publishCityId);//返回指定城市是否存在参数分类数据
//4.通过pb207的yjywflbm和ejywflbm,分别查询pb205一级业务分类表和pb206二级业务分类表,如果指定城市有数据就不发布,没数据就发布
Boolean aBoolean = exits.get();
if(!aBoolean){}
3.代码改进
之前之所以用异步,是因为要进入同事写的拦截方法,又不知道如何开启新事物
现在想起来,其实只要用@Transactional(propagation= Propagation.REQUIRES_NEW)开启新事物,就不用Future.get了
@Transactional(propagation= Propagation.REQUIRES_NEW)