36 }
37
38 public static void main(String[] args) {
39 ApplicationContext ctx =
40 new ClassPathXmlApplicationContext("com/baobaotao/connleak/applicatonContext.xml");
41 JdbcUserService userService = (JdbcUserService) ctx.getBean("jdbcUserService");
42
43 BasicDataSource basicDataSource = (BasicDataSource) ctx.getBean("dataSource");
44
45 //④汇报数据源初始连接占用情况
46 JdbcUserService.reportConn(basicDataSource);
47
48 JdbcUserService.asynchrLogon(userService, "tom");//启动一个异常线程A
49 JdbcUserService.sleep(500);
50
51 //⑤此时线程A正在执行JdbcUserService#logon()方法
52 JdbcUserService.reportConn(basicDataSource);
53
54 JdbcUserService.sleep(2000);
55
56 //⑥此时线程A所执行的JdbcUserService#logon()方法已经执行完毕
57 JdbcUserService.reportConn(basicDataSource);
58
59 JdbcUserService.asynchrLogon(userService, "john");//启动一个异常线程B
60 JdbcUserService.sleep(500);
61
62 //⑦此时线程B正在执行JdbcUserService#logon()方法
63 JdbcUserService.reportConn(basicDataSource);
64
65 JdbcUserService.sleep(2000);
66
67 //⑧此时线程A和B都已完成JdbcUserService#logon()方法的执行
68 JdbcUserService.reportConn(basicDataSource);
69
70 }
在JdbcUserService中添加一个可异步执行logon()方法的asynchrLogon()方法,我们通过异步执行logon()以及让主 线程睡眠的方式模拟多线程环境下的执行场景。在不同的执行点,通过reportConn()方法汇报数据源连接的占用情况。
通过Spring事务声明,对JdbcUserServie的logon()方法进行事务增强,配置代码如下所示:
01 < xml version="1.0" encoding="UTF-8" >
02
03 …
04 http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
05
06
07 08 destroy-method="close" 09 p:driverClassName="${jdbc.driverClassName}" 10 p:url="${jdbc.url}" 11 p:username="${jdbc.username}" 12 p:password="${jdbc.password}"/> 13 14 15 class="org.springframework.jdbc.core.JdbcTemplate" 16 p:dataSource-ref="dataSource"/> 17 18 19 class="org.springframework.jdbc.datasource.DataSourceTransactionManager" 20 p:dataSource-ref="dataSource"/> 21 22 23 24 然后,运行JdbcUserServie,在控制台将观察到如下的输出信息: 引用 我们通过表10-3对数据源连接的占用和泄漏情况进行描述。 active idle leak 可见在执行线程1执行完毕后,只释放了一个数据连接,还有一个数据连接处于active状态,说明泄漏了一个连接。相似的,执行线程2执行完毕后,也泄漏 了一个连接:原因是直接通过数据源获取连接(jdbcTemplate.getDataSource().getConnection())而没有显式释 放。 通过DataSourceUtils获取数据连接 Spring提供了一个能从当前事务上下文中获取绑定的数据连接的工具类,那就是DataSourceUtils。Spring强调必须使用 DataSourceUtils工具类获取数据连接,Spring的JdbcTemplate内部也是通过DataSourceUtils来获取连接 的。 DataSourceUtils提供了若干获取和释放数据连接的静态方法,说明如下:
连接数[active:idle]-[0:0]
连接数[active:idle]-[2:0]
连接数[active:idle]-[1:1]
连接数[active:idle]-[3:0]
连接数[active:idle]-[2:1]
时间 执行线程1 执行线程2 数据源连接
T0 未启动 未启动 0 0 0
T1 正在执行方法 未启动 2 0 0
T2 执行完毕 未启动 1 1 1
T3 执行完毕 正式执行方法 3 0 1
T4 执行完毕 执行完毕 2 1 2
static Connection doGetConnection(DataSource dataSource):首先尝试从事务上下文中获取连接,失败后再从数据源获取连接;
static Connection getConnection(DataSource dataSource):和doGetConnection方法的功能一样,实际上,它内部就是调用doGetConnection方法获取连接的;
static voi