@Transaction - 注解方式处理事务

本文介绍了一种使用自定义注解和动态代理实现Java事务管理的方法。通过创建Transaction注解和TxProxy代理类,可以在方法级别控制事务的开启和提交,简化了DAO层的事务处理过程。

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

1.自定义注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface Transaction {
}

2.动态代理类

public class TxProxy implements InvocationHandler {
	private Logger log = Logger.getLogger(TxProxy.class);
	private Object srcObject;
	private TxProxy(Object o) {
		this.srcObject = o;
	}
	@SuppressWarnings("unchecked")
	public static <T>T getProxy(Object src) {
		Object proxedObj = Proxy.newProxyInstance(
				TxProxy.class.getClassLoader(), src.getClass().getInterfaces(),
				new TxProxy(src));
		return (T)proxedObj;
	}
	@SuppressWarnings("unchecked")
	public static <T> T getProxy(Object src,Class<T> cls) {
		Object proxedObj = Proxy.newProxyInstance(
				TxProxy.class.getClassLoader(), src.getClass().getInterfaces(),
				new TxProxy(src));
		return (T)proxedObj;
	}
	@SuppressWarnings("unchecked")
	public static <T> T getProxy(Class<T> cls) {
		Object src=null;
		try {
			src = cls.newInstance();
		}catch(Exception e){
			e.printStackTrace();
		}
		Object proxedObj = Proxy.newProxyInstance(
				TxProxy.class.getClassLoader(), src.getClass().getInterfaces(),
				new TxProxy(src));
		return (T)proxedObj;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		if (method.isAnnotationPresent(Transaction.class)) {
			Connection conn = null;
			Object returnValue = null;
			try {
				conn = DataSourceUtils.getConnection();
				log.info("开始事务。连接对象为:"+conn);
				conn.setAutoCommit(false);
				returnValue = method.invoke(srcObject, args);
				log.info("提交一个事务");
				conn.commit();
			} catch (Exception e) {
				log.info("事务出错回滚");
				conn.rollback();
				throw new RuntimeException(e.getMessage(), e);
			} finally {
				log.info("将Connection放回到池中");
				conn.setAutoCommit(true);
				conn.close();
				/**
				 * 为了保证不出错,必须要remove一下
				 */
				DataSourceUtils.remove();
			}
			return returnValue;
		} else {
			log.info("不存在此注解,不开事务。");
			return method.invoke(srcObject, args);
		}
	}
}

3.使用,

在servlet中使用代理

要在@Transaction

public interface IOrderService {
	@Transaction
	public void saveOrder(Order order);
	
}

上面一段代码的DAO层的实现类,也就是可以不用管事务的任何事了,干干净净

	public void saveOrder(Order order) {
		//保存订单和订单明细
		String sql = "insert into orders(id,userid,consignee,paytype,amt,state,orderdate) " +
				"values(?,?,?,?,?,?,?)";
		QueryRunner run = new QueryRunner();
		run.update(getConnection(),sql, order.getId(),order.getUserid(),
				   order.getConsignee(),order.getPaytype(),order.getAmt(),
				   "0",order.getOrderdate());
		List<OrderLine> lines = order.getOrderLines();
		for(OrderLine ol:lines){
			sql = "insert into orderline(id,orderid,bookid,amt,price) values(?,?,?,?,?)";
			run.update(getConnection(), sql,ol.getId(),ol.getOrderid(),ol.getBookid(),ol.getAmt(),ol.getPrice());
		}
	}

 

<think>首先,用户的问题是:“我想了解在Spring框架中,当被调用的方法没有@Transaction注解时,事务是否会传播?请问Spring事务传播机制,被调用方法无@Transaction注解是否会继承事务?” 关键点是:被调用的方法没有@Transaction注解时,事务是否会传播?也就是说,调用者方法有事务,被调用者没有注解事务是否会继承? 根据提供的引用内容,我需要参考这些引用来构建回答。引用内容主要是关于Spring事务传播机制和@Transactional注解失效的场景。 回顾引用: - 引用[1]:介绍了@Transactional注解和传播机制,如REQUIRED(默认)和REQUIRES_NEW。 - 引用[2]:解释了在PROPAGATION_REQUIRED机制下,如果方法A有@Transactional,方法B被调用时,事务会传播。 - 引用[3]:讨论了同类方法调用时事务失效的问题,因为AOP代理没有被使用。 - 引用[4]:类似引用[3],说明当方法在同一个类中调用时,事务可能失效。 - 引用[5]:提到自定义实现事务传播行为,如REQUIRED和REQUIRES_NEW。 现在,针对用户的问题:当被调用的方法没有@Transaction注解时,事务是否会传播? 在Spring中,事务传播机制定义了事务如何传播到被调用的方法。传播行为是通过@Transactional注解的propagation属性指定的,默认是Propagation.REQUIRED。 - 如果调用者方法有事务(即,有@Transactional注解),并且传播机制是REQUIRED(默认),那么被调用的方法会加入现有事务,即使它自己没有@Transactional注解。这是因为事务是基于线程的,同一个线程内的多个方法可以共享一个事务上下文。 - 但是,有一个重要例外:如果被调用的方法是在同一个类中被调用,并且调用是通过this进行的(即,不是通过代理对象),那么事务可能不会传播,因为AOP代理没有被使用。这涉及到Spring的AOP机制。 从引用[2]:如果方法A有@Transactional,执行时传播给方法B,因为线程内的connection属性autoCommit=false,事务生效。 引用[2]说:如果方法A标注了@Transactional,执行时传播给方法B,因为方法A开启了事务,线程内的connection autoCommit=false,方法B使用同一个connection,所以事务传播生效。 但引用[3]和[4]强调,当调用发生在同一个类中时,事务可能失效,因为调用的是this对象的方法,而不是代理对象。 所以,核心点是: - 如果方法调用是通过代理对象(即,从Spring容器获取的bean),那么事务传播会正常工作。 - 如果方法调用是在同一个类中直接调用(this.callMethod()),那么事务传播可能不会生效,因为代理没有被触发。 对于用户的问题:被调用的方法没有@Transactional注解时,是否会继承事务- 在理想情况下(通过代理调用),如果调用者方法有事务,并且传播机制是REQUIRED,那么被调用方法会继承事务,即使它没有注解- 但在同一个类中调用时,可能不会继承,因为事务管理依赖于代理。 引用[2]直接支持:传播生效,如果调用是通过代理。 引用[3]和[4]解释失效场景。 所以,回答应该包括: 1. 事务传播机制概述。 2. 默认行为:REQUIRED传播。 3. 被调用方法无注解时,是否会继承事务:是的,在REQUIRED传播下,它会加入现有事务。 4. 但注意同类调用时的失效问题。 5. 提供解决方案或最佳实践。 回答结构: - 先解释Spring事务传播机制。 - 然后针对问题回答。 - 最后生成相关问题。 使用中文回答。 确保LaTeX格式正确:行内数学用$...$,独立公式用$$...$$。但在这个主题中,可能不需要数学公式,除非提到方程等。用户的问题是关于事务传播,没有明显数学内容,所以可能不需要LaTeX。但如果有变量或类似,可以用$...$。 例如,传播行为如REQUIRED,可以用文字描述。 生成相关问题:在回答末尾,添加相关问题。 引用标识:在回答中引用段落时,末尾添加[^n]。 现在,基于引用构建回答。 关键点: - 传播机制:REQUIRED(默认)表示如果当前有事务,则加入;否则新建。 - 被调用方法无注解:在REQUIRED下,如果调用者有事务,它会加入。 - 但失效场景:同类调用时,由于AOP代理问题。 从引用[2]: "如果方法 A 标注了注解 @Transactional是完全没问题的,执行的时候传播给方法 B,因为方法 A 开启了事务,线程内的 connection 的属性 autoCommit=false,并且执行到方法 B 时,事务传播依然是生效的,得到的还是方法 A 的connection,autoCommit 还是为 false,所以事务生效。" 这假设方法B是通过代理调用的。 引用[3]: "同类方法调用@Transaction注解的方法,事务失效...当调用带 @Transactional方法的调用方是同类方法时,调用的是this对象的方法,没有通过spring容器获取bean,就无法访问到代理对象,事务也就没有生效。" 所以,对于被调用方法无注解的情况: - 如果调用是通过代理(例如,不同类或通过Spring bean),事务会传播。 - 如果在同一个类中调用,事务可能不传播。 在用户的问题中,被调用方法无注解,但调用者可能有事务。 总结回答: - 在Spring中,事务传播机制定义了事务边界。 - 默认传播行为是Propagation.REQUIRED。 - 当被调用的方法没有@Transactional注解时: - 如果调用者方法有事务(有@Transactional),并且传播机制是REQUIRED,则被调用方法会继承并加入现有事务- 但是,如果调用发生在同一个类中(通过this调用),由于Spring AOP代理机制,事务可能不会传播,导致被调用方法在无事务上下文中执行。 - 解决方案:避免在同一个类中调用事务方法;使用代理或重构代码。 生成相关问题。</think>### Spring事务传播机制:被调用方法无@Transactional注解是否会继承事务 在Spring框架中,事务传播机制是管理事务边界的关键特性,它通过`@Transactional`注解的`propagation`属性定义。默认传播行为是`Propagation.REQUIRED`(如果当前存在事务,则加入;否则新建事务)。针对您的问题——当被调用的方法没有`@Transactional`注解时,事务是否会传播(即继承调用者的事务)——答案取决于调用方式和传播机制。以下我将逐步解释核心机制、行为分析、失效场景及解决方案,确保回答基于Spring官方机制和引用内容。 #### 1. **Spring事务传播机制概述** - Spring事务基于AOP(面向切面编程)实现,通过动态代理(CGLIB或JDK代理)管理事务。当事务方法被调用时,代理对象在方法执行前后添加事务逻辑(如开启事务、提交或回滚)[^3][^4]。 - 传播行为定义了事务如何在方法间传递: - `REQUIRED`(默认):如果当前有事务,则加入;否则新建事务- `REQUIRES_NEW`:始终新建事务,挂起当前事务(如果存在)。 - 其他行为如`SUPPORTS`、`MANDATORY`等,但`REQUIRED`是最常见的默认设置[^1]。 - 事务上下文与线程绑定:同一个线程内的方法共享事务状态(如数据库连接的`autoCommit=false`)[^2][^5]。 #### 2. **被调用方法无@Transactional注解时的行为** 当被调用的方法没有`@Transactional`注解,但调用者方法有该注解时,事务是否传播取决于传播机制和调用方式- **在默认传播机制(REQUIRED)下,事务通常会传播(继承)**: - 如果调用者方法有`@Transactional`(使用`REQUIRED`),被调用方法会**自动加入现有事务**,即使它没有注解。这是因为事务上下文基于线程共享:调用者开启事务后,线程的数据库连接(`autoCommit=false`)会被复用,被调用方法在同一个事务上下文中执行[^2][^5]。 - 示例:方法A有`@Transactional`,调用方法B(无注解)。在代理模式下,方法B会继承方法A的事务。 ```java @Service public class MyService { @Transactional(propagation = Propagation.REQUIRED) // 默认传播行为 public void methodA() { // 开启事务 methodB(); // 方法B无@Transactional,但加入同一事务 } public void methodB() { // 执行数据库操作,继承methodA的事务 } } ``` - **原因**:Spring通过代理对象管理事务。当从Spring容器获取bean时,实际调用的是代理对象,事务逻辑被注入。被调用方法复用调用者的事务资源(如数据库连接),因此事务传播生效[^2][^5]。 - **例外情况:事务可能不传播(失效)**: - **同类方法调用(通过`this`)**:如果调用发生在同一个类中(例如,方法A调用方法B,且都定义在同一类),事务**不会传播**。因为调用通过`this`对象(非代理),代理逻辑未被触发,被调用方法在无事务上下文中执行(`autoCommit=true`)[^3][^4]。 - 示例失效场景: ```java @Service public class MyService { @Transactional public void methodA() { this.methodB(); // 同类调用,事务不传播 } public void methodB() { // 无事务上下文,操作自动提交 } } ``` - **原因**:Spring AOP代理仅在通过容器bean调用时生效。同类`this`调用绕过代理事务管理失效[^3][^4]。 - **其他传播行为**:如果传播机制设为`REQUIRES_NEW`,被调用方法会新建事务,不继承调用者事务(无论是否有注解)[^1][^5]。但这不直接适用于无注解场景。 #### 3. **事务失效的解决方案** 针对同类调用导致的事务传播失效问题,推荐以下实践: - **避免同类调用**:将事务方法拆分到不同类中,确保调用通过Spring代理- 重构示例: ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; // 注入另一个bean @Transactional public void methodA() { serviceB.methodB(); // 通过代理调用,事务传播生效 } } @Service public class ServiceB { public void methodB() { // 无注解,但继承事务 } } ``` - **使用自注入或AOP代理**:通过`@Autowired`注入自身bean,强制代理调用。 ```java @Service public class MyService { @Autowired private MyService self; // 自注入代理 @Transactional public void methodA() { self.methodB(); // 通过代理调用 } public void methodB() { // 事务生效 } } ``` - **显式添加注解**:如果被调用方法需要独立事务控制,添加`@Transactional`并指定传播行为(如`REQUIRED`)[^1][^4]。 #### 4. **总结** - **事务传播**:在默认`REQUIRED`机制下,被调用方法无`@Transactional`注解时,**会继承调用者的事务**(前提是调用通过代理对象)。 - **失效风险**:同类调用(`this.method()`)会导致事务不传播,因为AOP代理未被使用。 - **最佳实践**:确保事务方法通过Spring bean调用,避免同类依赖;在需要时显式使用`@Transactional`控制传播行为[^2][^4][^5]。 如果您有具体代码场景,可以提供更多细节,我可以进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值