1、使用LinkedList保存连接
即使是最简单的JDBC操作,也需要包含以下几步:建立连接、创建SQL语句、执行语句、处理执行结果、释放资源,其中建立连接步骤是很耗费计算机性能的,如果我们每次进行JDBC操作都创建新的JDBC连接,使用完后再立即释放连接,这样做会耗费大量性能。更合理的做法应该是:创建JDBC连接,使用JDBC连接,使用完后不是立刻释放JDBC连接,而是把连接缓存起来,当下一次操作JDBC时,我们可以直接使用缓存中已经连接上的JDBC连接。
以下的代码通过一个LinkedList保存创建的JDBC连接,在刚创建的时候,我们会先建立10个连接并保存在list中,当客户代码需要使用Connection的时候,我们直接从list中取出第一个Connection返回,当客户代码使用完Connection,需要调用free()方法,把Connection再放回list中:
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
|
public
class DataSource1 {
LinkedList<
Connection
> connectionPool = new LinkedList<
Connection
>();
public
DataSource1() {
try {
for
(
int
i = 0; i < 10; i++) {
this.connectionPool.addLast(this.createConnection());
}
} catch (SQLException e) {
throw new ExceptionInInitializerError(e);
}
}
public
Connection
getConnection() {
return
connectionPool.removeFirst();
}
public
void
free
(
Connection
conn) {
connectionPool.addLast(conn);
}
private
Connection
createConnection() throws SQLException {
"root"
,
""
);
}
}
|
2、控制连接数量
在上例的getConnection()方法中,我们没有检测connectionPool中是否包含Connection,直接返回connectionPool中的第一个Connection,这样做是不安全的,在从connectionPool中返回Connection之前,我们首先应该检测connectionPool中是否有Connection,若有,返回connectionPool中的第一个Connection,如果没有,我们可以新创建一个并返回。但是这样有一个问题,如果请求的线程很多,我们这样无限制地创建很多Connection可能导致数据库阻塞,因为数据库可以支持的连接数是有限的,所以我们应该控制新建Connection的上限,若没有达到上限,我们可以创建并返回,如果达到连接上限,那么我们就抛出异常。同时我们应该在getConnection()上加锁,保证多线程安全性,修改后代码如下,我们用initCount表示list初始化大小,maxCount表示list最大大小,currentCount表示现在存活的Connection数量:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public
class
DataSource2 {
private
static
int
initCount =
10
;
private
static
int
maxCount =
30
;
private
int
currentCount =
0
;
LinkedList<Connection> connectionPool =
new
LinkedList<Connection>();
public
DataSource2() {
try
{
for
(
int
i =
0
; i < initCount; i++) {
this
.connectionPool.addLast(
this
.createConnection());
this
.currentCount++;
}
}
catch
(SQLException e) {
throw
new
ExceptionInInitializerError(e);
}
}
public
Connection getConnection()
throws
SQLException {
synchronized
(connectionPool) {
if
(connectionPool.size() >
0
)
return
connectionPool.removeFirst();
if
(
this
.currentCount < maxCount) {
this
.currentCount++;
return
createConnection();
}
throw
new
SQLException(
"已没有链接"
);
}
}
public
void
free(Connection conn) {
connectionPool.addLast(conn);
}
private
Connection createConnection()
throws
SQLException {
"root"
,
""
);
}
}
|
3、动态代理拦截close方法
上面的代码有个问题,那就是要关闭Connection必须调用我们的free()方法,不能直接调用Connection上的close()方法,这对于一些习惯使用完Connection就close()的用户并不是很友好,很容易导致它们忘记把用完的Connection还回connectionPool,为了让用户保持原有习惯,我们希望能够改写Connection的close()方法,让它不是直接关闭连接,而是把连接还回connectionPool中,其他方法保持原来不变,对于这种需求,我们可以使用动态代理实现:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
public
class
DataSource3 {
private
static
int
initCount =
1
;
private
static
int
maxCount =
1
;
int
currentCount =
0
;
LinkedList<Connection> connectionsPool =
new
LinkedList<Connection>();
public
DataSource3() {
try
{
for
(
int
i =
0
; i < initCount; i++) {
this
.connectionsPool.addLast(
this
.createConnection());
this
.currentCount++;
}
}
catch
(SQLException e) {
throw
new
ExceptionInInitializerError(e);
}
}
public
Connection getConnection()
throws
SQLException {
synchronized
(connectionsPool) {
if
(
this
.connectionsPool.size() >
0
)
return
this
.connectionsPool.removeFirst();
if
(
this
.currentCount < maxCount) {
this
.currentCount++;
return
this
.createConnection();
}
throw
new
SQLException(
"已没有链接"
);
}
}
public
void
free(Connection conn) {
this
.connectionsPool.addLast(conn);
}
private
Connection createConnection()
throws
SQLException {
Connection realConn = DriverManager.getConnection(
MyConnectionHandler proxy =
new
MyConnectionHandler(
this
);
return
proxy.bind(realConn);
}
}
class
MyConnectionHandler
implements
InvocationHandler {
private
Connection realConnection;
private
Connection warpedConnection;
private
DataSource3 dataSource;
MyConnectionHandler(DataSource3 dataSource) {
this
.dataSource = dataSource;
}
Connection bind(Connection realConn) {
this
.realConnection = realConn;
this
.warpedConnection = (Connection) Proxy.newProxyInstance(
this
.getClass().getClassLoader(),
new
Class[] { Connection.
class
},
this
);
return
warpedConnection;
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
if
(
"close"
.equals(method.getName())) {
this
.dataSource.connectionsPool.addLast(
this
.warpedConnection);
}
return
method.invoke(
this
.realConnection, args);
}
}
|
上述代码的关键点是MyConnectionHandler类,在该类的bind()方法中返回了一个wrapedConnection,wrapedConnection的创建方法如下:
1
|
this
.warpedConnection = (Connection) Proxy.newProxyInstance(
this
.getClass().getClassLoader(), <br>
new
Class[] { Connection.
class
},
this
);
|
Proxy.newProxyInstance()方法是Java动态代理的关键方法,该方法会在运行时在内存中动态创建一个类,该方法的第一个参数是指定一个ClassLoader,第二个参数指定动态创建类实现的接口,第三个参数指定在该类上调用的方法应该转给哪个类,在这里指定了this,所以所有方法都会转给MyConnectionHandler,准确的说是MyConnectionHandler的invoke方法:
1
2
3
4
5
6
7
|
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
if
(
"close"
.equals(method.getName())) {
this
.dataSource.connectionsPool.addLast(
this
.warpedConnection);
}
return
method.invoke(
this
.realConnection, args);
}
|
在invoke()方法中,我们可以看到第二个参数传递了调用的Method,我们根据传递的Method对象判断,用户调用的是否是close方法,如果是close方法,那么我们就把这个Connection重新加入list,如果不是,那么我们就执行真正Connection上的相应方法。
还需要注意的是,现在调用createConnection()产生的Connection对象已经不是原始的Connection对象,而是调用MyConnectionHandler类上bind()方法动态产生的代理类:
1
2
3
4
5
6
|
private
Connection createConnection()
throws
SQLException {
Connection realConn = DriverManager.getConnection(
MyConnectionHandler proxy =
new
MyConnectionHandler(
this
);
return
proxy.bind(realConn);
}
|