笔记摘要:
这里首先对数据库连接池的优化进行了说明,同时自己编写了一个数据库连接池,在实际开发中,为了获取标准的数据源,我们需要去实现javax.sal.DataSource接口,
在实现过程中对于链接对象的close方法进行了不同的实现,以便在关闭close的时候,将连接对象放回连接池,而不是关闭掉,针对这一问题,提供了3种不同的解决
方案,涉及了3种设计模式:装饰,适配器和代理。
一、直接获取连接与使用连接池的比较
应用程序直接获取连接示意图
缺点:
用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。
数据库连接池示意图
优势:
连接池中会有指定个数的连接对象,每次连接的时候,只要将请求发给连接池,连接池就会提供连接对象,使用完之后,再将连接对象放回连接池,
这样不用每次都去连接,大大提高了性能。
二、编写一个基本的连接池实现连接复用
原理:
通过一个LinkedList来模拟连接池,每次取Connection的时候,就remove,当释放的时候就再add进去,这里通过打印remove的前后,来说明每次使用之后会放回“连接池”
dbinfo.properties:
className=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb
user=root
password=root
自定义连接池
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.ResourceBundle;
import javax.management.RuntimeErrorException;
public class SimpleConnectionPool {
private static String className;
private static String url;
private static String user;
private static String password;
//创建一个集合用于模拟连接池
private static LinkedList pool = new LinkedList();
// private static LinkedList pool = (LinkedList) Collections.synchronizedCollection(new LinkedList());
//加载配置文件并注册驱动
static{
try {
ResourceBundle bundle = ResourceBundle.getBundle("cn.itmonkey.util.dbinfo");
className = bundle.getString("className");
url = bundle.getString("url");
user = bundle.getString("user");
password = bundle.getString("password");
Class.forName(className);
//创建10个连接对象
for(int i=0;i<10;i++){
Connection conn = DriverManager.getConnection(url,user,password);
pool.add(conn);
}
System.out.println("初始化连接");
//打印所创建的连接对象
int i=0;
for(Connection conn: pool){
System.out.println(conn+"..."+i++);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
//获取连接对象,由于可能同时会有多个对象来取,所以使用同步
public synchronized static Connection getConnection(){
System.out.println("取之前的连接如下");
int i=0;
for(Connection conn: pool){
System.out.println(conn+"..."+i++);
}
//从队列中移出一个连接对象,返回给调用者
if(pool.size()>0){ //防止为0的时候,导致异常,所以要进行判断
Connection conn = pool.remove();
return conn;
}
else{
throw new RuntimeException("对不起,服务器忙!");
}
}
//将连接对象释放到队列中
public static void release(Connection conn){
pool.addLast(conn);
}
}
二、编写标准的数据库连接池
标准的数据源:
需要实现javax.sal.DataSource接口的类,标准的数据源里默认了维护了一个连接池
问题:
在直接获取标准数据源的Connection后,在调用它的close方法时,不是还回连接池中,而是直接关闭,我们希望在调用close方法的时候,是将Connection还回连接池中,而不是关闭
解决方案一:装饰设计模式
通过装饰,对需要的已有功能进行增强
装饰设计模式编写步骤
1、编写一个类,实现与被增强对象相同的接口
2、定义一个引用变量,记住被增强对象
3、定义构造方法,参数为接口后者父类,比便实现多态,传入被增强对象, 并给第2部的变量赋值
4、对于要增强的方法,自己改写
5、对于不需要增强的方法,调用原有对象的对应方法。
public class MyConnection implements Connection{
private Connection conn;
private LinkedList pool;//模拟连接池
public MyConnection(Connection conn,LinkedList pool){
this.conn = conn;
this.pool = pool;
}
//释放连接对象到连接池
@Override
public void close() throws SQLException {
pool.add(conn);
}
@Override
public void clearWarnings() throws SQLException {
conn.clearWarnings();
}
@Override
public void commit() throws SQLException {
conn.commit();
}
//不需要增强的方法,调用原有对象的对应方法即可,
@Override
public Array createArrayOf(String typeName, Object[] elements)
throws SQLException {
return conn.createArrayOf(typeName, elements);
}
//后面有很多方法,因为不需要增强的方法,调用原有对象的对应方法即可,这里略去
…………
}
解决方案二:适配器模式
通过一个类去继承一个接口或父类,对需要增强的方法进行改写即可,适配器模式在监听机制中出现比较多,由于父类中有很多的抽象方法,如果一一实现,比较麻烦,所以通常在API中会提供一个已经默认实现的类,我们只需去继承这个类,然后对希望增强的方法进行复写即可
适配器模式编写步骤
1.编写一个类,继承默认适配器
2.定义一个引用变量,记住被增强对象
3.定义构造方法,传入被增强的对象,并给第2部的变量赋值
4.对于要增强的方法,自己改写
为Connection准备的适配器
适配器本身也是一个包装类,实现或者继承一个类,但是什么都不做,所有的方法都调用原有对象的对应方法
public class ConnectionWrapper implements Connection{
protected Connection conn;
public ConnectionWrapper(Connection conn){
this.conn = conn;
}
@Override
public void clearWarnings() throws SQLException {
conn.clearWarnings();
}
@Override
public void close() throws SQLException {
conn.close();
}
@Override
public void commit() throws SQLException {
conn.commit();
}
@Override
public Array createArrayOf(String typeName, Object[] elements)
throws SQLException {
return null;
}
…………
}
继承适配器,对需要增强的方法复写即可
public class MyConnection2 extends ConnectionWrapper {
private LinkedList pool;
public MyConnection2(Connection conn, LinkedList pool) {
super(conn);
this.pool = pool;
}
//将连接对象还回池中
public void close() throws SQLException {
pool.add(conn);
}
}
解决方案三:动态代理
使用动态代理,在获取Connection的方法中使用代理,所以在获取Connection的时候,就是一个代理的Connection,该代理的Connection对象,会对调用方法进行判断,如果是close方法,就对返回值进行改写(这里是将Connection对象放回连接池中),否则就按照原来方法去执行
i
mport java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.ResourceBundle;
import javax.sql.DataSource;
//标准的数据源
public class ProxyDataSource implements DataSource{
private static String className;// 驱动类名
private static String url;// 连接串
private static String user;
private static String password;
private static LinkedList pool = new LinkedList();
static {
try {
ResourceBundle rb = ResourceBundle.getBundle("cn.itxushuai.util.dbinfo");
className = rb.getString("className");
url = rb.getString("url");
user = rb.getString("user");
password = rb.getString("password");
Class.forName(className);
// 初始化10个连接
for (int i = 0; i < 10; i++) {
Connection conn = DriverManager.getConnection(url, user,
password);
pool.add(conn);
}
} catch (Exception e) {
throw new ExceptionInInitializerError("驱动加载失败");
}
}
@Override
public synchronized Connection getConnection() throws SQLException {
if(pool.size()>0){
System.out.println("池中的连接如下");
int i=1;
for(Connection conn : pool){
System.out.println(conn+"----"+i++);
}
final Connection conn = pool.remove();
Connection proxyConn = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(),
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if("close".equals(method.getName())){
return pool.add(conn);
}else{
return method.invoke(conn, args);
}
}}
);
return proxyConn;
}else{
throw new RuntimeException("服务器忙");
}
}
@Override
public Connection getConnection(String username, String password)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public T unwrap(Class iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
}