第八章:基础拦截器-2. AOP拦截器

本文详细介绍了如何在Spring框架中利用AOP(面向切面编程)对业务层进行拦截处理,包括创建业务接口和服务实现,通过配置AspectJ表达式实现业务调用前后的日志记录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在spring里面还提供有一种aop拦截器配置,不过大部分的aop拦截器都是围绕着业务层进行拦截处理的。

1.建立一个普通的业务操作接口和它的子类:

package cn.mldn.microboot.service;

import cn.mldn.microboot.vo.Member;

public interface IMemberService {
	public Member get(long mid) ;
}
package cn.mldn.microboot.service.impl;

import org.springframework.stereotype.Service;

import cn.mldn.microboot.service.IMemberService;
import cn.mldn.microboot.vo.Member;

@Service
public class MemberServiceImpl implements IMemberService {
	@Override
	public Member get(long mid) {
		Member vo = new Member();
		vo.setMid(mid);
		vo.setName("KING");
		vo.setSalary(50000.00);
		return vo;
	}

}

现在业务层的操作完成之后,去修改控制层,让控制层进行业务层的调用

package cn.mldn.microboot.controller;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import cn.mldn.microboot.service.IMemberService;
import cn.mldn.microboot.util.controller.AbstractBaseController;
import cn.mldn.microboot.vo.Member;

@Controller
public class MemberController extends AbstractBaseController {
	@RequestMapping(value = "/member_add_pre", method = RequestMethod.GET)
	public String memberAddPre() {
		return "member_add";
	}
	@RequestMapping(value = "/member_add", method = RequestMethod.POST)
	@ResponseBody
	public Object memberAdd(Member member) {
		return member ;
	}
	@Resource
	private IMemberService memberService ;
	@RequestMapping(value = "/member_get", method = RequestMethod.GET)
	@ResponseBody
	public Object get(long mid) {
		return this.memberService.get(mid) ;
	}
}

7db3bf3b5b34fad181df30f70453bbeeccf.jpg

3.现在的业务层只是纯粹的调用而已,但是现在希望对调用的过程进行拦截处理,所以要想实现这样的处理,那么就需要引入新的依赖包,修改pom.xml的配置文件:

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

4.编写一个aop拦截的控制程序类

package cn.mldn.microboot.config;

import java.util.Arrays;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ServiceAspect { // 此时定义有一个业务层的拦截处理
	private Logger log = LoggerFactory.getLogger(ServiceAspect.class);
	@Around("execution(* cn.mldn..service..*.*(..))")
	public Object arroundInvoke(ProceedingJoinPoint point) throws Throwable {
		this.log.info("【*** Service-Before ***】执行参数:"
				+ Arrays.toString(point.getArgs()));
		Object obj = point.proceed(point.getArgs()); // 进行具体业务调用
		this.log.info("【*** Service-After ***】返回结果:" + obj);
		return obj;
	}
}

对于web拦截可以使用拦截器,而对于业务层的拦截可以使用Aspect表达式结合aop的拦截机制实现。

转载于:https://my.oschina.net/u/3023191/blog/3036373

2025-03-31 15:54:16.254 INFO 33208 --- [nio-8081-exec-1] i.seata.tm.api.DefaultGlobalTransaction : [47.99.131.207:8099:36663551609720840] rollback status: Rollbacked 2025-03-31 15:54:16.264 ERROR 33208 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/cart-service] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause java.lang.ArithmeticException: / by zero at com.bye.service.impl.CartServiceImpl.selectCartByUserId(CartServiceImpl.java:38) ~[classes/:na] at com.bye.service.impl.CartServiceImpl$$FastClassBySpringCGLIB$$b4100ead.invoke(<generated>) ~[classes/:na] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.24.jar:5.3.24] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.24.jar:5.3.24] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.24.jar:5.3.24] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.24.jar:5.3.24] at io.seata.spring.annotation.GlobalTransactionalInterceptor$2.execute(GlobalTransactionalInterceptor.java:205) ~[seata-all-1.5.2.jar:1.5.2] at io.seata.tm.api.TransactionalTemplate.execute(TransactionalTemplate.java:127) ~[seata-all-1.5.2.jar:1.5.2] at io.seata.spring.annotation.GlobalTransactionalInterceptor.handleGlobalTransaction(GlobalTransactionalInterceptor.java:202) ~[seata-all-1.5.2.jar:1.5.2] at io.seata.spring.annotation.GlobalTransactionalInterceptor.invoke(GlobalTransactionalInterceptor.java:172) ~[seata-all-1.5.2.jar:1.5.2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.24.jar:5.3.24] at org.springframework.aop.framework.CglibA
最新发布
04-01
<think>好的,我现在需要帮助用户解决两个问题:排查Java中的ArithmeticException错误原因,以及分析Seata分布式事务回滚的日志问题。首先,我得先理解用户提供的引用内容,看看已有的信息有哪些,然后逐步分析。 首先,关于ArithmeticException: / by zero,引用[1]、[2]、[3]、[4]都提到了这个错误是由于除以零导致的。用户需要排查这个错误的原因。根据引用[3]和[4],解决方法通常是在除法运算前检查除数是否为零,或者进行异常捕获。但用户可能已经知道这些,现在需要更深入的排查步骤,比如如何定位到具体的代码位置,或者是否有其他潜在原因。 然后是关于Seata分布式事务回滚的日志分析。用户提到涉及GlobalTransactionalInterceptor、Spring AOP、CGLIB代理等。需要结合这些技术点,分析在分布式事务回滚时,日志中的异常处理情况,尤其是当ArithmeticException发生时,Seata的事务回滚是否正常触发,以及相关日志是否被正确记录。 接下来,我需要分步骤处理这两个问题: 1. **ArithmeticException排查**: - 确认错误堆栈中的具体位置,比如引用[1]中的报错位置是`BigDecimal.divide`方法,可能是在做除法时没有设置精度或处理除数为零的情况。 - 检查所有涉及除法运算的代码,特别是使用BigDecimal的地方,确保除数不为零,或者使用带有精度和舍入模式的方法。 - 添加日志或断点,在可能发生除法的地方记录除数的值,帮助定位问题。 2. **Seata事务回滚日志分析**: - Seata的事务回滚通常依赖于业务方法抛出异常。如果业务方法中有未捕获的异常(如ArithmeticException),Seata的拦截器应该会触发回滚。 - 需要检查GlobalTransactionalInterceptor的日志,确认在抛出ArithmeticException时,是否进入了回滚流程。 - 由于Spring AOP使用CGLIB代理,要确保代理生效,事务注解(如@GlobalTransactional)被正确应用,避免因为AOP代理问题导致事务未回滚。 可能的问题点: - 用户可能在某个服务方法中进行了除法运算,而没有处理除数为零的情况,导致异常抛出。 - 该异常未被捕获,触发了Seata的事务回滚,但日志中没有明确的事务回滚记录,或者用户不确定回滚是否成功。 需要结合用户的具体代码和日志来分析。例如,在引用[1]的报错中,除法操作是在处理统计信息时发生的,可能与数据库查询结果有关,比如返回了零值,导致后续计算百分比时分母为零。此时需要检查查询结果的处理逻辑,确保分母不为零,或者在代码中添加条件判断。 对于Seata部分,需要确认事务拦截器是否正确地捕获了异常,并执行了回滚操作。查看日志中是否有Seata相关的回滚信息,比如“Rollback global transaction”等。同时,检查Spring的代理配置,确保@GlobalTransactional注解的方法被正确代理,避免因AOP问题导致事务未生效。 总结步骤: - 定位除法错误的具体位置,检查除数来源。 - 添加防御性代码,处理除数为零的情况。 - 检查Seata事务日志,确认回滚是否触发。 - 确保AOP代理正确,事务注解生效。 这样用户就能逐步排查并解决问题了。</think>### 排查 `ArithmeticException` 错误原因 #### 1. **错误定位与分析** - **错误本质**:`java.lang.ArithmeticException: / by zero` 表示代码中发生了除数为零的运算。例如: ```java int a = 10 / 0; // 直接触发异常 BigDecimal result = value1.divide(value2); // 若 value2=0 且未设置舍入模式,也会报错[^1][^2] ``` - **排查步骤**: 1. **检查堆栈信息**:根据报错日志(如引用[1])定位到具体代码行。例如: ```java at java.math.BigDecimal.divide(BigDecimal.java:5214) // 分母为0的BigDecimal运算 ``` 2. **验证分母来源**:检查分母变量是否可能为0,尤其是动态计算结果(如数据库查询结果、用户输入等)。例如,统计计算中分母为0会导致百分比异常[^2]。 3. **防御性编程**: - **条件判断**:在除法前添加 `if (分母 != 0)`。 - **使用安全方法**:对 `BigDecimal` 运算指定舍入模式: ```java value1.divide(value2, RoundingMode.HALF_UP); // 显式设置舍入规则[^3] ``` #### 2. **代码修复示例** ```java // 修复前(风险代码) BigDecimal result = value1.divide(value2); // 修复后(安全代码) if (value2.compareTo(BigDecimal.ZERO) == 0) { result = BigDecimal.ZERO; // 或自定义处理逻辑 } else { result = value1.divide(value2, 2, RoundingMode.HALF_UP); } ``` --- ### Seata 分布式事务回滚日志分析 #### 1. **Seata 事务回滚触发条件** - **事务回滚逻辑**:当 `@GlobalTransactional` 注解的方法抛出异常时,Seata 的 `GlobalTransactionalInterceptor` 会拦截异常并触发分布式事务回滚[^4]。 - **关键日志特征**:成功回滚时,日志会包含以下信息: ```log Rollback global transaction [xxx] for exception: java.lang.ArithmeticException ``` #### 2. **日志排查步骤** 1. **确认事务拦截器是否生效**: - 检查 `GlobalTransactionalInterceptor` 是否被 Spring AOP 代理正确加载。若使用 CGLIB 代理,需确保类未被 `final` 修饰且方法非 `private`。 - 验证 `@GlobalTransactional` 注解是否添加在 `public` 方法上(Spring AOP 对非 public 方法可能失效)。 2. **分析异常传播路径**: - 确保 `ArithmeticException` 未被 `try-catch` 捕获并“吞没”(若异常未传播到拦截器,事务不会回滚)。 - 检查日志中是否有 `TransactionException` 或 `Rollback` 关键字,确认回滚动作是否执行。 #### 3. **代码与配置建议** - **事务注解示例**: ```java @GlobalTransactional public void businessMethod() { // 业务逻辑(若此处抛出未捕获的异常,Seata 触发回滚) } ``` - **代理配置**:若使用 CGLIB,在 Spring Boot 主类添加 `@EnableAspectJAutoProxy(proxyTargetClass = true)`。 --- ### 整合排查流程 1. **优先修复 `ArithmeticException`**:确保所有除法操作有分母为0的防护逻辑[^3]。 2. **验证 Seata 事务行为**: - 在修复异常后,通过日志确认事务是否正常提交或回滚。 - 若回滚未触发,检查代理配置和异常传播逻辑。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值