首页 > 其他分享 >【异常管理(全局异常处理器)】|【事务管理(使用AOP)】

【异常管理(全局异常处理器)】|【事务管理(使用AOP)】

时间:2024-11-12 19:51:00浏览次数:3  
标签:事务管理 ... 方法 id AOP 异常 public

本篇主要介绍项目开发中两个比较基础但是非常重要的模块,异常管理和事务管理,如何去使用进行了介绍,着重对AOP的使用进行了介绍,实现一个基于AOP的简单案例:定位耗时较长的业务方法,统计部分业务方法的执行耗时。

一、全局异常处理器

需求:软件开发springboot项目过程中,不可避免的需要处理各种异常,spring mvc架构中各层会出现大量的try{...} catch{...} finally{...}代码块,不仅有大量的冗余代码,而且还影响代码的可读性。这样就需要定义个全局统一异常处理器,以便业务层再也不必处理异常。

 具体例子如下:

在部门管理系统中,存在了一个就业部,此时再添加一个就业部之后,就会响应500:

查看服务端报错显示duplicate,也就是重复了,因为在数据库建表的时候,给name设置了unique约束,唯一约束。所以出现异常的时候前端不能对返回的结果进行正确的接收以及处理。

所以要进行异常处理:

首先出现了异常其实是一步步向上抛的,mapper向service抛,然后service向controller层抛,然后controller层向上抛,就产生了这种类似500等错误。如果全写到controller层中try...catch...处理,代码非常臃肿,所以最好的方法就是定义一个全局异常处理器,在controller层抛出之后丢给全局异常处理器,然后经过处理之后,返回设定好的类型json格式数据给前端响应过去。

实现方式:

用@RestControllerAdvice+@ExceptionHandler来实现全局异常处理器:
异常处理最好是解耦的,并且都放在一个地方集中管理:
@ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
@RestControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开,这个注解包含两部分@RestControllerAdvice=@ControllerAdvice+@ResponseBody;可以将java对象数据类型转化为json格式的数据相应给前端。
@ResponseStatus:可以将某种异常映射为HTTP状态码

只使用@ExceptionHandler,只能在当前Controller中处理异常,与@ControllerAdvice结合可以实现全局异常处理,不用每个controller都配置。

下面是一个简单的例子,其中也可以去自定义异常然后在方法中进行判断,然后响应给前端相应的信息:

@RestControllerAdvice
public class GlobeExceptionHandler {
    @ExceptionHandler(Exception.class)
    public Result ex(Exception ex){
        ex.printStackTrace();
        return Result.error("对不起,操作失败,请联系管理员");
    }
}

二、事务管理

事务具有ACID(原子性,一致性,隔离性,持久性)四个特性,将多个操作合并为一个整体,如果过程中出现了异常,进行rollback回滚,保证全部成功或者全部失败。

同样举个例子来展开说明:

删除部门操作:删除部门后,部门内的员工也要同时删除,但是此时有一些风险,在删除完部门之后发生了异常无法执行删除员工操作,但是得到的结果是删除了部门,员工没有删除,所以要将删除部门和删除员工归为一个事务,要么同时成功要么同时失败;此时就用到了事务,使用@Transactional注解实现:

 

rollbackFor属性:

如果在代码部分加上1/0这个异常,抛出异常之后,还是会出现删除掉了部门但是没有删除该部门的员工,因为默认情况下只有出现RuntimeException才回滚异常。

想要将所有的异常都回滚,就要使用rollbackFor属性,案例代码如下:

Controller层:

//TODO 删除指定部门:
    @MyLog
    //@DeleteMapping("/depts/{id}")
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id){//PathBariable注解将路径中的id绑定到参数id;
        log.info("根据参数id删除部门:{}",id);
        deptService.delete(id);
        return Result.success();
    }

Service层: 

@Transactional(rollbackFor = Exception.class)//spring事务管理
    @Override
    public void delete(Integer id) {
        try {
            deptMapper.delete(id);
//现在进行完善删除部门操作,继续删除该部门所在的员工:
            empMapper.deleteByDeptId(id);
        }finally {//继续完善操作,记录操作的日志,不管删除成功与否,都将操作记录下来。因为是成功失败都要记录,所以要将这部分代码加到finally中
            DeptLog deptLog=new DeptLog();
            deptLog.setCreateTime(LocalDateTime.now());
            deptLog.setDescription("执行了解散部门的操作,此次解散的是"+id+"号部门");
            deptLogService.insert(deptLog);
      
        }
    }

propagation属性:

事务传播行为:当一个事务中调用了另一个事务的方法,这个方法该如何去进行事务控制。

属性值需要注意REQUIRED和REQUIRES_NEW两个:

案例如下:

finally中:上方service层中调用了setDescription记录操作日志的方法,这个是必须要执行的,无论是否发生异常,但是仔细想如果前面失败这里也要执行的话,这个方法需要用到事务管理,并且需要自己新开一个事务,因为如果跟着这个delete方法事务的话,执行失败了还会进行回滚,所以执行的写入数据库的操作也会回滚,记录也就消失了,所以需要新开一个事务,要用到propagation属性,方法如下:

@Service
public class DeptLogServiceImpl implements DeptLogService {
    @Autowired
    private DeptLogMapper deptLogMapper;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insert(DeptLog deptLog) {
        deptLogMapper.insert(deptLog);
    }
}

上面事务管理的底层就是通过AOP来实现的,将代码的前面和后面添加开启事务和回滚事务或者提交事务。 

 AOP

面向切面编程(AOP),本质上就是面向特定的方法编程。

采用AOP其实是生成了一个代理对象,将目标对象的方法进行了扩充,然后后面使用依赖注入其实注入的是代理对象,最后执行的也是代理对象中的方法。

AOP的优点就是:代码无侵入,减少重复代码,提高开发效率,维护方便。

AOP核心概念:

切入点就是实际被AOP增强的方法。

例如计算程序运行时间,那么通知就是这些重复的逻辑,给方法前面加上时间后面加上时间,最后相减;

连接点:可以被AOP控制的方法

通知所应用的对象就是目标对象,如下代码中DeptserviceImpl就是目标对象

通知类型:

切入点表达式:

@Pointcut注解:有多个连接点方法时候比较方便:
@Slf4j
@Component
//@Aspect
public class MyAspect1 {
    //可以采用@Pointcut注解,将公共部分的切入点表达式抽取出来,下面直接引用这个切入点表达式,就不用一个个都写了,如果要改的话,只需要改这一个地方即可;
    @Pointcut("execution(* com.springboot_test2.service.impl.DeptServiceImpl.*(..))")
    private void pt(){}//此处用private修饰,只能在这个切面类中调用,如果改为public的话,可以在TImeAspect切面类中使用。

    @Before("pt()")
    public void before_test(){
        log.info("before...");
    }
    @Around("pt()")
    public Object around_test(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("around before...");
        //调用目标对象的原始方法执行
        Object result = proceedingJoinPoint.proceed();
        log.info("around after...");
        return result;
    }
    @After("pt()")
    public void after_test(){
        log.info("after...");
    }
    @AfterReturning("pt()")
    public void afterReturning_test(){
        log.info("afterReturning...");
    }
    @AfterThrowing("pt()")
    public void afterThrowing_test(){
        log.info("afterThrowing...");

    }


}

 执行顺序:

由案例展开介绍:

部分业务耗时较长,在逐个方法中去实现计时代码非常冗余,所以想把这部分通用的业务代码抽离出来,无需每个方法都去单独写一个计时操作。这就可以使用AOP来实现:

首先使用AOP之前需要导入AOP的依赖,然后根据业务需要编写AOP程序。

然后在业务类上需要添加@Component注解,将该对象注入到容器中;添加@Aspect注解,表示是AOP切面类。中间@Around注解里面的内容代表想要执行哪一个方法。

代码如下:自定义一个MyLog annotation搭配使用

@Slf4j
@Component
@Aspect
public class LogAspect {
    //TODO 这个过程中需要调用OperateLogMapper中的方法插入日志表,所以要进行依赖注入
    @Autowired
    private OperateLogMapper operateLogMapper;
    @Autowired
    private HttpServletRequest request;
    @Around("@annotation(com.springboot_test.anno.MyLog)")//这样指的是匹配加了Mylog注解的方法。
    public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
        //操作人ID以及姓名-当前登录员工的ID:  想要获取ID和姓名可以想到在完成登录后会生成jwt令牌,jwt令牌中携带username以及id,可以解析jwt令牌然后获取这两个数据:
        //获取jwt令牌得需要先获取请求对象,HttpServletRequest;
        String jwt=request.getHeader("token");
        Claims claims = JwtUtils.parseJWT(jwt);
        Integer id = (Integer) claims.get("id");
        String username=(String)claims.get("username");
        //操作时间
        LocalDateTime now = LocalDateTime.now();
        //操作类名
        String className = joinPoint.getClass().getName();
        //操作方法名
        String methodName = joinPoint.getSignature().getName();
        //方法参数
        Object[] args = joinPoint.getArgs();
        String paramName = Arrays.toString(args);
        //方法执行
        long begin = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end=System.currentTimeMillis();
        //方法返回值
        String returnValue=JSONObject.toJSONString(result);
        //操作耗时
        Long costTime=end-begin;
        OperateLog operateLog=new OperateLog(null,id,username,now,className,methodName,paramName,returnValue,costTime);
        operateLogMapper.insert(operateLog);
        log.info("AOP记录操作日志"+operateLog);
        return result;




    }
}

标签:事务管理,...,方法,id,AOP,异常,public
From: https://blog.csdn.net/m0_57957187/article/details/143715107

相关文章

  • 信步漫谈之自定义AOP注释
    目录1目标2AOP基本概念3AOP之Execution表达式解释4AOP注释执行顺序5知识池6源码7参考资料(感谢)1目标自定义注释的AOP实现2AOP基本概念Aspect【切面】:通常是一个类,里面可以定义切入点和通知;JointPoint【连接点】:程序执行过程中明确的点,一般是方法的调用;Advice......
  • 插件工厂和AOP拦截器机制
    一.插件工厂基于接口机制的插件工厂是一种设计模式,用于创建和管理插件。插件是独立的模块,可以动态加载和卸载,以扩展应用程序的功能。通过使用接口,插件工厂可以提供一种统一的方式来创建和管理这些插件,而不需要了解插件的具体实现细节。具体实现来说,实现一个插件工厂通过哈希记......
  • 如何捕获和处理HTTP GET请求的异常
    在开发网络应用程序时,处理HTTP请求和响应是核心功能之一。特别是,GET请求是Web开发中最常见的请求类型之一。然而,网络请求可能会因为多种原因失败,比如网络问题、服务器错误、或者请求超时等。因此,有效地捕获和处理这些异常对于构建健壮的应用程序至关重要。本文将介绍如何......
  • 安全通道异常识别系统
    安全通道异常识别系统通过安装在消防通道附近的监控摄像头,安全通道异常识别系统对安全通道进行24小时不间断的监控。当系统检测到防火门被异常开启或安全通道被堵塞时,会立即启动警告机制,通过声音、灯光等方式提醒相关人员进行处理。同时,系统还会将异常信息实时传输到监控中心,以便......
  • java浅拷贝BeanUtils.copyProperties引发的RPC异常
    作者:京东物流吴义背景近期参与了一个攻坚项目,前期因为其他流程原因,测试时间已经耽搁了好几天了,本以为已经解决了卡点,后续流程应该顺顺利利的,没想到人在地铁上,bug从咚咚来~没有任何修改的服务接口,抛出异常:java.lang.ClassCastException:java.util.HashMapcannotbecastto......
  • 如何处理微信小程序大量未捕获的异常
    1)如何处理微信小程序大量未捕获的异常2)如何关闭代码创建的纹理的读写,或者创建不带读写的图片3)回收带有贴图和Collider的Mesh,如何正确用对象池维护4)Cloth组件使用在一个篮筐上,运行后篮网扭曲,是什么原因这是第408篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社......
  • golang异常处理trycatch,确保系统不崩溃
    packagemainimport(  "fmt"  "os"  "os/signal"  "syscall")functest0(){  deferfunc(){    ifr:=recover();r!=nil{//捕获panic      fmt.Printf("CaughtintryCatch:%v\n&q......
  • Python捕获与处理异常
        在Python中,异常处理是一种重要的机制,用于处理程序运行时可能出现的错误情况。对程序的异常捕获与处理,可增强程序稳定性、可读性与可维护性,实现优雅的错误恢复。一、异常的概念    异常是程序在运行过程中发生的错误或意外情况。当出现异常时,程序的正常执行......
  • windows C#-异常处理
    C#程序员使用try块来对可能受异常影响的代码进行分区。关联的catch块用于处理生成的任何异常。finally块包含无论try块中是否引发异常都会运行的代码,如发布try块中分配的资源。try块需要一个或多个关联的catch块或一个finally块,或两者皆之。下面的示例演示......
  • 反射API与AOP在日志记录与审计中的应用案例
    引言在现代软件开发中,日志记录和审计是两个非常重要的功能,它们帮助开发者监控系统行为、追踪错误和安全问题。反射API和面向切面编程(AOP)是实现这些功能的强大工具。本文将探讨如何结合这两种技术来提高日志记录和审计的灵活性和效率。反射API在日志记录中的应用反射API允许......