@Configuration的实现原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @EnableTransactionManagement
@Component("com.example.config") public class JdbcConfig { @Bean public JdbcTemplate jdbcTemplate(){ return new JdbcTemplate(dataSource()); }
@Bean public PlatformTransactionManager transactionManager(){ DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource()); return transactionManager; }
@Bean public DataSource dataSource(){ DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("xxx"); dataSource.setUrl("xxx"); dataSource.setUsername("xxx"); dataSource.setPassword("xxx"); return dataSource; } }
|
在上面的场景中,配置了JdbcTemplate的配置项,但没有@Configuration,也会出现一个事务失效问题:
1 2 3 4 5
| @Transactional public void b(){ jdbcTemplate.queryForList("update emp set name='小明' where emp_id=2"); throw new RuntimeException(); }
|
jdbcTemplate.queryForList执行完直接提交,后续抛出异常也无法回滚数据。
再重温一遍事务的启动机制:
- 事务管理器新建一个数据库连接conn,不用jdbcTemplate建连接,因为其默认autocommit=true,执行完会直接提交
- 设置conn.autocommit = false;
- jdbc通过ThreadLocal<Map<DataSource, conn>>获取conn执行sql语句
- 如果一切正常,conn.commit(),否则conn.rollback()
事务启动时事务管理器(PlatformTransactionManager)会新建一个数据库连接,将其存入ThreadLocal<Map<DataSource, conn>>中,然后取消事务自动提交,当jdbc需要时直接从ThreadLocal中获取。
此时的问题主要是,因为没有配置@Configuration,当代理对象执行到b()方法,然后执行jdbc数据库方法,此时jdbc获取conn的原理实际上是new JdbcTemplate(dataSource()),而事务管理器中transactionManager.setDataSource(dataSource()),相当于是两个不同的dataSource对象,而jdbc的dataSource对象默认autocommit=true,当执行完sql语句,直接提交,导致后续无法回滚
1 2 3 4 5 6 7 8 9 10 11
| @Bean public JdbcTemplate jdbcTemplate(){ return new JdbcTemplate(dataSource()); }
@Bean public PlatformTransactionManager transactionManager(){ DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource()); return transactionManager; }
|
那么为什么标注了@Configuration之后,就能解决这个问题呢?其原理到底是什么?
@Configuration的原理也是基于动态代理的(AOP、@Lazy都是基于动态代理),底层原理大致如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Class JdcbConfigProxy extends JdbcConfig{ JdbcConfig target; public JdbcTemplate jdbcTemplate(){ . return super.jdbcTemplate(); } public DataSource dataSource(){ if(...){ ... return ioc.getBean(); } else { DataSource dataSource = super.dataSource(); ioc.putBean(dataSource); return dataSource; } } }
|
基于动态代理,不论是jdbc还是transactionManager,其获取dataSource最终都要先去ioc容器中找,若找到
就直接返回,找不到在调用dataSource()方法创建。