springboot(2.0)配置日志插入数据库,发生错误时脏数据回滚,日志不回滚。

springboot(2.0)配置日志插入数据库

github:配置日志插入数据库操作
项目名:springboot-jpa-aop-Log

注意事项和重点

  1. 新增方法不要加try catch做异常捕获,不然无法触发@AfterThrowing异常处理方法(或者创建新的异常让@AfterThrowing捕获,同时手动做回滚)TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  2. 注意在日志保存操作时使用 (@Transactional(propagation = Propagation.REQUIRES_NEW))配置另起新的事务,这样保存事务才不会被影响到。

1.项目结构和配置顺序

这里写图片描述

1.1创建实体类

表结构图:
这里写图片描述
这里写图片描述

1.2创建dao层接口

1.3配置注解和注解的实现类

1.4编写service进行注解

2.配置项目

主要讲解和注解实现类的配置,实体类和接口请在github上查看
注解

//作用位置。
@Target({ElementType.PARAMETER, ElementType.METHOD})
//注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysServiceLog {

    /**
     * 操作类型,类型为1:增加,2:删除,3:查询,4:修改,5:其他
     * @return
     */
    String oprlogType() default "";
    /**
     * 操作备注
     * @return
     */
    String oprlogRemark() default "";

    //操作模块
    String oprlogModule() default "";

}

注解类

@Component
@Aspect
public class SysServiceLogAspect {

    @Resource
    private SysOprlogService sysOprlogService;

    private static String[] types = { "java.lang.Integer", "java.lang.Double",
            "java.lang.Float", "java.lang.Long", "java.lang.Short",
            "java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
            "java.lang.String", "int", "double", "long", "short", "byte",
            "boolean", "char", "float" ,"java.util.HashMap"};
    private static final Logger logger = LoggerFactory.getLogger(SysServiceLogAspect.class);

    // Service层切点
    @Pointcut("@annotation(SysServiceLog)")
    public void serviceAspect() {
    }

    /**
     * 返回通知,拦截方法执行成功后的结果,保存日志
     * @param joinPoint 切点
     * @param result 方法执行结果
     */
    @AfterReturning(value="serviceAspect()",returning="result")
    public void doServiceAfterReturning(JoinPoint joinPoint, Object result) {
        SysOprlog sysOprlog = getSysOprlogInfo(joinPoint);
        sysOprlog.setOprlogDescription(sysOprlog.getOprlogRemark() + "成功!");
        sysOprlog.setOprlogStatus("1");
        saveSysOprlog(sysOprlog);
    }

    /**
     * 异常通知 用于拦截service层记录异常日志
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        String className = joinPoint.getTarget().getClass().getName();// 获取所在实体类
        String methodName = joinPoint.getSignature().getName();// 获取方法名
        logger.error("执行失败!"+className+"->"+methodName+"失败原因:" + e.getMessage());
        SysOprlog sysOprlog = getSysOprlogInfo(joinPoint);
        sysOprlog.setOprlogDescription(sysOprlog.getOprlogRemark() + "失败!");
        sysOprlog.setOprlogStatus("0");
        saveSysOprlog(sysOprlog);
    }

    /**
     * 获取SysOprlog的属性内容
     * @param joinPoint
     * @return
     */
    private SysOprlog getSysOprlogInfo(JoinPoint joinPoint){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        // 读取session中的用户
//        SysUser user = (SysUser) session.getAttribute("user");
        //获取的是本地的IP地址 //PC-20140317PXKX/192.168.0.121
        String ip = IPUtils.getIp(request);

        String className = joinPoint.getTarget().getClass().getName();// 获取所在实体类
        String methodName = joinPoint.getSignature().getName();// 获取方法名

        SysOprlog sysOprlog = new SysOprlog();
        try {
            String paramContent = getLogParamContent(getFieldsName(this.getClass(), className, methodName), joinPoint);
            //获取注解描述
            SysServiceLog systemServiceLog = getServiceMethodDescription(joinPoint);
            sysOprlog.setOprlogId(UUID.randomUUID().toString().replace("-", ""));
            sysOprlog.setOprlogParam(paramContent);
            sysOprlog.setOprlogRemark(systemServiceLog.oprlogRemark());
            sysOprlog.setOprlogTime(new Date());
            sysOprlog.setOprlogType(systemServiceLog.oprlogType());
            sysOprlog.setOprlogModuleName(systemServiceLog.oprlogModule());
//            sysOprlog.setOprlogLoginIp(ip);
        } catch (Exception e) {
            // 记录本地异常日志
            logger.error("获取失败!失败原因:" + e.getMessage());
        }
        return sysOprlog;
    }

    /**
     * 获取注解中对方法的描述信息,用于service层注解
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    private SysServiceLog getServiceMethodDescription(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();

        SysServiceLog systemServiceLog = null;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    systemServiceLog = method.getAnnotation(SysServiceLog.class);
                    break;
                }
            }
        }
        return systemServiceLog;
    }

    /**
     * 获取调用方法参数的名称
     * @param cls
     * @param clazzName
     * @param methodName
     * @return
     * @throws NotFoundException
     */
    @SuppressWarnings("rawtypes")
    private String[] getFieldsName(Class cls, String clazzName, String methodName) throws NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);

        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramNames.length; i++){
            paramNames[i] = attr.variableName(i + pos); //paramNames即参数名
        }
        return paramNames;
    }

    /**
     * 获取日志参数内容。格式:参数名+参数内容
     * @param paramNames
     * @param joinPoint
     * @return
     */
    private String getLogParamContent(String[] paramNames, JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        boolean clazzFlag = true;
        for(int k=0; k<args.length; k++){
            Object arg = args[k];
            sb.append(paramNames[k]);
            // 获取对象类型
            String typeName = arg.getClass().getName();
            for (String t : types) {
                if (t.equals(typeName)) {
                    sb.append("=" + arg+";");
                    clazzFlag = false;
                    break;
                }
            }
            if (clazzFlag) {
                sb.append(getFieldsValue(arg));
            }
        }
        return sb.toString();
    }

    /**
     * 得到参数的值
     * @param obj
     */
    private String getFieldsValue(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        String typeName = obj.getClass().getName();
        for (String t : types) {
            if(t.equals(typeName))
                return "";
        }
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (Field f : fields) {
            f.setAccessible(true);
            try {
                for (String str : types) {
                    if (f.getType().getName().equals(str)){
                        if(f.get(obj) != null) {
                            sb.append(f.getName() + " = " + f.get(obj)+"; ");
                        }
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        sb.append("]");
        return sb.toString();
    }

    /**
     * hessian请求存储操作日志
     * @param sysOprlog
     */
    private void saveSysOprlog(SysOprlog sysOprlog){
        try {
            sysOprlogService.save(sysOprlog);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("保存操作日志失败!失败原因:" + e.getMessage());
        }
    }
}

service配置重点

userService
@Service("userService")
public class UserService {

    private Logger logger = LoggerFactory.getLogger(UserService.class);

    @Resource
    private UserRepository userRepository;

    @Transactional
    @SysServiceLog(oprlogType="1",oprlogRemark="新增/修改用户",oprlogModule = "系统管理")
    public void insert(UsersEntity user){

//        try {
            userRepository.save(user);
//            异常测试代码
//            ArrayList list = new ArrayList(2);
//            System.out.println(list.get(11));


/*         注意使用try catch捕捉异常无法启动SysServiceLogAspect的@AfterThrowing方法。                              */
//        }catch (RuntimeException e){
//            logger.info("用户保存失败");
//            //就是这一句了,加上之后,抛了异常,  会回滚
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
//        }finally{
//
//        }
    }
}

SysOprlogService
@Service("sysOprLogService")
public class SysOprlogService {

    @Resource
    private SysOprLogRepository sysOprLogRepository;

//    配置另起新的事务,这样保存事务才不会被影响到。
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save(SysOprlog sysOprlog){
        sysOprLogRepository.save(sysOprlog);
    }

}

3.测试结果

测试方法

    @Test
    public void save(){
        UsersEntity usersEntity = new UsersEntity();
        usersEntity.setId(55);
        usersEntity.setUserName("124");
        usersEntity.setPassWord("124");
        usersEntity.setNickName("124");
        usersEntity.setUserSex("女");
        userService.insert(usersEntity);

    }

正常新增结果

这里写图片描述

错误新增结果

这里写图片描述

数据库结果

这里写图片描述
这里写图片描述

4.事务的说明

4.1事务回滚必要条件

  1. 被拦截方法-—— 注解式:方法或者方法所在类被@Transactional注解;
  2. 异常—— 该方法的执行过程必须出现异常,这样事务管理器才能被触发,并对此做出处理
  3. 指定异常—— 默认配置下,事务只会对Error与RuntimeException及其子类这些UNChecked异常,做出回滚。 一般的Exception这些Checked异常不会发生回滚(如果一般Exception想回滚要做出配置);
    举例:实现一般异常的回滚:
    注解式:@Transactional(rollbackFor=Exception.class)

  4. 异常抛出—— 即方法中出现的指定异常,只有在被事务管理器捕捉到以后,事务才会据此进行事务回滚;
    如:

1,不捕捉,会回滚
2.如果异常被try{}捕捉到,那么事务管理器就无法再捕捉异常,所以就无法做出反应,事务不回滚;(既catch(RuntimeException))
3.如果异常被try{}捕捉了,我们还可以在Catch(){}中throw new RuntimeException(),手动抛出运行时异常供事务管理器捕捉;(既catch(){ throw new RuntimeException(); } )
4.手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值