对于 @Transactional 注解,Spring 会根据目标对象是否实现了接口来决定使用哪种代理方式。如果目标对象实现了接口,Spring 会使用 JDK 动态代理;否则,它会使用 CGLIB 代理。
因此,@Transactional 注解的实现并不完全基于 JDK 接口代理,它还支持基于类的代理(通过 CGLIB)。具体使用哪种代理方式取决于目标对象是否实现了接口。
| <dependencies> |
| <dependency> |
| <groupId>org.springframework</groupId> |
| <artifactId>spring-jdbc</artifactId> |
| <version>6.1.8</version> |
| </dependency> |
| |
| <dependency> |
| <groupId>mysql</groupId> |
| <artifactId>mysql-connector-java</artifactId> |
| <version>8.0.29</version> |
| </dependency> |
| |
| <dependency> |
| <groupId>com.alibaba</groupId> |
| <artifactId>druid</artifactId> |
| <version>1.1.17</version> |
| </dependency> |
| |
| <dependency> |
| <groupId>jakarta.annotation</groupId> |
| <artifactId>jakarta.annotation-api</artifactId> |
| <version>2.1.1</version> |
| </dependency> |
| </dependencies> |
| package com.powernode.bank.dao; |
| |
| |
| import com.powernode.bank.pojo.Account; |
| |
| public interface AccountDao { |
| |
| Account selectByActno(String actno); |
| |
| |
| int update(Account act); |
| |
| |
| int insert(Account act); |
| } |
| package com.powernode.bank.dao.impl; |
| |
| import com.powernode.bank.dao.AccountDao; |
| import com.powernode.bank.pojo.Account; |
| import jakarta.annotation.Resource; |
| import org.springframework.jdbc.core.BeanPropertyRowMapper; |
| import org.springframework.jdbc.core.JdbcTemplate; |
| import org.springframework.stereotype.Repository; |
| |
| @Repository |
| public class AccountDaoImpl implements AccountDao { |
| |
| @Resource |
| private JdbcTemplate jdbcTemplate; |
| |
| |
| @Override |
| public Account selectByActno(String actno) { |
| String sql = "select actno,balance from t_act where actno = ?"; |
| Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), actno); |
| System.out.println("笑"+account); |
| return account; |
| |
| } |
| |
| @Override |
| public int update(Account act) { |
| String sql = "update t_act set balance = ? where actno = ?"; |
| int update = jdbcTemplate.update(sql, act.getBalance(), act.getActno()); |
| return update; |
| } |
| |
| @Override |
| public int insert(Account act) { |
| String sql = "insert into t_act values(?,?)"; |
| return jdbcTemplate.update(sql,act.getActno(),act.getBalance()); |
| |
| } |
| } |
| package com.powernode.bank.pojo; |
| |
| public class Account { |
| private String actno; |
| private Double balance; |
| |
| public Account() { |
| } |
| |
| public Account(String actno, Double balance) { |
| this.actno = actno; |
| this.balance = balance; |
| } |
| |
| public String getActno() { |
| return actno; |
| } |
| |
| public void setActno(String actno) { |
| this.actno = actno; |
| } |
| |
| public Double getBalance() { |
| return balance; |
| } |
| |
| public void setBalance(Double balance) { |
| this.balance = balance; |
| } |
| |
| @Override |
| public String toString() { |
| return "Account{" + |
| "actno='" + actno + '\'' + |
| ", balance=" + balance + |
| '}'; |
| } |
| } |
| package com.powernode.bank.service; |
| |
| import com.powernode.bank.pojo.Account; |
| |
| public interface AccountService { |
| void transfer(String fromActno, String toActno, double money); |
| |
| void save(Account act); |
| } |
| package com.powernode.bank.service.impl; |
| |
| import com.powernode.bank.dao.AccountDao; |
| import com.powernode.bank.pojo.Account; |
| import com.powernode.bank.service.AccountService; |
| import jakarta.annotation.Resource; |
| import org.springframework.stereotype.Service; |
| import org.springframework.transaction.annotation.Propagation; |
| import org.springframework.transaction.annotation.Transactional; |
| import org.springframework.transaction.event.TransactionalEventListener; |
| |
| @Service |
| public class AccountServiceImpl implements AccountService { |
| @Resource |
| private AccountDao accountDao; |
| |
| |
| @Override |
| @Transactional |
| public void transfer(String fromActno, String toActno, double money) { |
| |
| Account fromAct = accountDao.selectByActno(fromActno); |
| if (fromAct.getBalance() < money){ |
| throw new RuntimeException("余额不足!!"); |
| } |
| |
| Account toAct = accountDao.selectByActno(toActno); |
| |
| fromAct.setBalance(fromAct.getBalance() - money); |
| toAct.setBalance(toAct.getBalance() + money); |
| |
| |
| int count = accountDao.update(fromAct); |
| |
| |
| |
| |
| |
| count += accountDao.update(toAct); |
| |
| |
| |
| if (count !=2){ |
| throw new RuntimeException("转账失败,联系银行"); |
| } |
| } |
| |
| @Resource(name = "accountServiceImpl2") |
| private AccountService accountService; |
| |
| @Override |
| @Transactional(propagation = Propagation.REQUIRED) |
| public void save(Account act) { |
| |
| accountDao.insert(act); |
| |
| |
| Account act2 = new Account("act-004", 1000.0); |
| try{ |
| accountService.save(act2); |
| }catch (Exception e){ |
| |
| } |
| |
| } |
| } |
| package com.powernode.bank.service; |
| |
| import com.alibaba.druid.pool.DruidDataSource; |
| import org.springframework.context.annotation.Bean; |
| import org.springframework.context.annotation.ComponentScan; |
| import org.springframework.context.annotation.Configuration; |
| import org.springframework.jdbc.core.JdbcTemplate; |
| import org.springframework.jdbc.datasource.DataSourceTransactionManager; |
| import org.springframework.transaction.annotation.EnableTransactionManagement; |
| |
| import javax.sql.DataSource; |
| import javax.xml.crypto.Data; |
| |
| |
| @Configuration |
| @ComponentScan(value = {"com.powernode.bank"}) |
| @EnableTransactionManagement |
| public class Spring6Config { |
| |
| |
| @Bean(name = {"druidDataSource2"}) |
| public DruidDataSource getDataSource(){ |
| DruidDataSource druidDataSource = new DruidDataSource(); |
| druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); |
| druidDataSource.setUrl("jdbc:mysql://192.168.1.41:3306/test"); |
| druidDataSource.setUsername("root"); |
| druidDataSource.setPassword("Abc123***"); |
| return druidDataSource; |
| } |
| |
| @Bean(name = "jdbcTemplate") |
| |
| public JdbcTemplate getJdbcTemplate(DataSource druidDataSource2){ |
| JdbcTemplate jdbcTemplate = new JdbcTemplate(); |
| jdbcTemplate.setDataSource(druidDataSource2); |
| return jdbcTemplate; |
| } |
| |
| @Bean(name = "txManager") |
| public DataSourceTransactionManager getDataSourceTransactionManager(DataSource druidDataSource2){ |
| DataSourceTransactionManager txManager = new DataSourceTransactionManager(); |
| txManager.setDataSource(druidDataSource2); |
| return txManager; |
| } |
| |
| } |
| @Test |
| public void testNoXML(){ |
| AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Spring6Config.class); |
| AccountService accountServiceImpl = annotationConfigApplicationContext.getBean("accountServiceImpl", AccountService.class); |
| accountServiceImpl.transfer("act-001","act-002",10000); |
| System.out.println("转账成功"); |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)