Paul Paul Public Builder & 全栈工程师
search
arrow_back 返回标签拓扑

Java

共 2 篇。

2022-07-02 · 约 4 分钟

避坑:@Around与@Transactional混用导致事务不回滚

前言 上个月,同事出于好奇在群里问AOP的环绕通知与事务注解混合用会不会导致出现异常不回滚的情况。这个问题我一下子回答不上来,因为平时没这样用过,在好奇心的驱使下,我调试了半天终于得到结果,今天我就展开讲讲。(源码解读在最后面,感兴趣的可以看看。) 结论 首先告诉大家的是,同时使用AOP环绕通知和事务注解之后,最终生成的拦截器链的相对顺序是事务的拦截器在前面,AOP环绕通知的拦截器在后面。 在事务的实现中将拦截器的执行过程包裹在了try-catch块中,发生异常后根据配置来决定是否回滚事务。(详见org.springframework.transaction.interceptor.TransactionInterceptor#invoke),因此事务后面的拦截器都会影响事务的执行结果。如果在AOP环绕通知里面将拦截器链执行结果中的异常给吞掉,那么事务就会正常提交而不会回滚。 示例 业务代码 业务代码中直接抛出异常,代码如下所示。 java content_copy 复制代码 package com.example.demo.aspect; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * @Author Paul * @Date 2022/7/3 15:52 */ @Service public class CustomService { @Transactional(rollbackFor = Exception.class) public void echo(){ boolean s = true; if (s){ throw new RuntimeException("test"); } System.out.println("Hello------"); } } 环绕通知 环绕通知中捕捉异常并打印日志,代码如下所示。 java content_copy 复制代码 package com.example.demo.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * @Author Paul * @Date 2022/7/3 15:49 */ @Component @Aspect public class CustomAspect { @Pointcut("execution(* com.example.demo.aspect..*(..))") public void pointcut(){ } @Around("pointcut()") public void around(ProceedingJoinPoint joinPoint){ try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } } } 测试类 这里是用get请求来测试(本来应该用 unit test 来测试的,但是懒得写代码了,手动测试和 UT 的效果一样),代码如下所示。

阅读全文 arrow_forward
2022-05-10 · 约 1 分钟

《阿里巴巴java规范》 Result 方式杂谈

前言 上个月开始和同事一起做一个为期一个月的项目。期间同事看了我的代码,给我投来了一个奇异的眼神,问道“你的代码没有错误码吗?我调你的方法,我怎么知道成功与否呢?”,今天我就这个话题来讨论一下为什么系统内部调用时不需要封装请求结果,出错直接抛出异常就好。 核心争论点 系统的内部调用是否需要封装错误码以及请求返回结果。换句话说,我同事认为一个成功的系统调用需要像下面这样返回数据对象(这里只是以json作为示例,并不代表我们调用的结果是json)。 json content_copy 复制代码 { "code": 200, "success": true, "msg": "", "data": {} } 而一个失败的系统调用是否又需要像下面这样返回。 json content_copy 复制代码 { "code": 500, "success": false, "msg": "error message", "data": {} } 使用结果类 这里我觉得可能她是受《阿里巴巴开发手册》里面的一条关于 RPC 调用规范的影响 跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess()方法、“错误码”、“错误 简短信息”;而应用内部推荐异常抛出。 说明:关于 RPC 方法返回方式使用 Result 方式的理由: 1)使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。 2)如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message,对于调用端解决问题 的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。 系统内部调用要使用异常 文中也提到,系统内部调用还是应该通过抛出 异常 的方式,而不是用 Result 方式来返回错误结果。 (这里的 Result 方式就是上文我们提到的,使用结果类来封装请求结果)。 在系统内部使用 异常,相对于使用 Result 方式来说有个非常明显的优点–在使用异常时,不需要对调用成功与否进行判断。 如果调用失败直接抛出异常,调用者处理这个异常就好。 看到这里大家可能有疑惑,这么简单的问题为什么还需要和同事讨论? 我认为有这个讨论的根源问题在于每个人开发习惯的不同。方法调用时同事更喜欢使用 Result 方式来处理,而我更喜欢使用 异常 的方式。 其实,我这个时候做个让步也没什么,毕竟只有提供给她的部分函数需要按照这个方式,改动量不大。但是最近我在读 Sonmez 的《Soft Skills》,我对里面提到的一个观点很有感触,或许是受这个观点潜移默化的影响,我觉得有必要坚持我的观点。

阅读全文 arrow_forward