源码及配置文件:https://download.csdn.net/download/u010452388/10394985,有问题可以留言沟通哈
一 为什么要用连接池?
1.1 优化前
1.当jdbc程序每次访问数据库都需要创建一个新的连接,访问完毕之后,需要释放资源。
2.那么在这样的一个过程中,连接的创建和销毁所消耗的资源是远远大于我们发送sql并执行的时间的。
3.基于这样的情况,我们发现我们的jdbc程序将大量的资源浪费在了连接的创建和销毁上
所以我们要对这样的结构进行优化
1.2 优化后
首先创建一定数量的连接,然后放到指定的地方。当我们需要获取连接的时候,直接从指定的地方获取。用完了,我们再将连接放回去。这样就能首先我们连接的回收利用。并且不用花费时间在创建和销毁连接上。
二、动态代理(核心)
1、调用Connection的close方法,不是关闭连接,而是将连接还给连接池。
2、使用动态代理可以重写类的方法,
动态代理作用:不改变原类的代码与不使用继承就可以对类的功能 进行增强。
三、数据库连接池实现的步骤
1我们自定义的数据库连接池需要实现java.sql.DataSource这个接口。这是sun公司规定。
备注: DriverManager 是JDBC 1.0规范,可以创建连接,但是不支持连接池
DataSource是JDBC 2.0规范,支持连接池
2.我们使用linkedList来存放连接。当做连接池。(想想为什么用LinkedList而不用ArrayList? 下面代码注释有解释)
3.在dataSource的构造方法中初始化数据库连接池。向池子中创建一定数量的数据库连接对象。
4.重写DataSource这个接口中的getConnection方法。注意:这个方法是从我们数据库连接池中获取数据,从连接池中获取之后需要将池子中的对象给删掉
5.提供释放资源的方法。注意:这个释放资源是将连接放回连接池中,而不是关闭连接。
四、代码实现步骤
4.1 mysql数据表
create database day05;
use day05;
create table emp(
id int primary key auto_increment,
name varchar(50),
city varchar(50)
);
insert into emp values(null, '刘备', '北京');
insert into emp values(null, '关羽', '上海');
insert into emp values(null, '张飞', '广州');
4.2 Jdbc.properties配置文件
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day05
user=root
pwd=123
4.3 编写静态代码块
当类加载的时候会执行静态代码块的内容。并完成以下两件事:
1. 读取配置文件信息
2. 注册mysql驱动
public static String driverClass = null;
public static String url = null;
public static String user = null;
public static String pwd = null;
// 注册驱动
static {
//创建配置文件对象
Properties p = new Properties();
try {
//加载src下的配置文件信息
p.load(new FileInputStream("src/jdbc.properties"));
driverClass = p.getProperty("driverClass");
url = p.getProperty("url");
user = p.getProperty("user");
pwd = p.getProperty("pwd");
// 注册驱动
Class.forName(driverClass);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
4.4 编写构造函数代码
当创建对象的时候,执行此代码内容,完成一下内容:
1.创建拥有5个连接的连接池
// 声明连接池变量,由于操作连接池主要是增加,删除操作,
//LinkedList是增删块,查询慢;ArrayList增删慢,查询块,所以这里选用LinkedList
private LinkedList<Connection> pools = new LinkedList<Connection>();
// 创建对象时,连接池中就有5个连接了
public MyDataSource() throws SQLException {
for (int i = 0; i < 5; i++) {
Connection conn = DriverManager.getConnection(url, user, pwd);
// 这里添加的时候添加到第一个,后面获取的时候,获取最后一个
pools.addFirst(conn);
}
System.out.println("连接池初始化:"+pools.size());
}
4.5 关键点-动态代理重写close方法
此步骤中要对Connection的close方法进行重写,但是其他的方法应该保持原有的功能,该怎么处理?
我们可以用继承去重写,但是这里Connection是一个接口,无法继承,所以我们可以使用动态代理实现close方法,即当调用者调用close方法关闭连接的时候,实际上是将连接放回到连接池,而不是关闭连接
@Override
public Connection getConnection() throws SQLException {
// 获取的时候,获取最后一个连接
final Connection conn = pools.removeLast();
System.out.println("获得连接后,连接池有:"+pools.size());
ClassLoader loader = conn.getClass().getClassLoader();
Class<?>[] interfaces = conn.getClass().getInterfaces();
InvocationHandler h = new InvocationHandler() {
//对conn对象的方法进行重写
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断conn对象执行的方法是不是close方法,如果是,则将连接返回到连接池,而不是关闭连接
if("close".equals(method.getName())){
pools.addFirst(conn);
System.out.println("关闭后连接池有:"+pools.size());
return null;
}else{
return method.invoke(conn, args);
}
}
};
Connection proxyConn = (Connection) Proxy.newProxyInstance(loader, interfaces, h);
return proxyConn;
}
4.6 自定义连接池完整代码
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import 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.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.logging.Logger;
import javax.sql.DataSource;
public class MyDataSource implements DataSource {
public static String driverClass = null;
public static String url = null;
public static String user = null;
public static String pwd = null;
// 注册驱动
static {
//创建配置文件对象
Properties p = new Properties();
try {
//加载src下的配置文件信息
p.load(new FileInputStream("src/jdbc.properties"));
driverClass = p.getProperty("driverClass");
url = p.getProperty("url");
user = p.getProperty("user");
pwd = p.getProperty("pwd");
// 注册驱动
Class.forName(driverClass);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 声明连接池变量,由于操作连接池主要是增加,删除操作,
//LinkedList比ArrayList增删快,所以这里选用LinkedList
private LinkedList<Connection> pools = new LinkedList<Connection>();
// 创建对象时,连接池中就有5个连接了
public MyDataSource() throws SQLException {
for (int i = 0; i < 5; i++) {
Connection conn = DriverManager.getConnection(url, user, pwd);
// 这里添加的时候添加到第一个,后面获取的时候,获取最后一个
pools.addFirst(conn);
}
System.out.println("连接池初始化:"+pools.size());
}
@Override
public Connection getConnection() throws SQLException {
// 获取的时候,获取最后一个连接
final Connection conn = pools.removeLast();
System.out.println("获得连接后,连接池有:"+pools.size());
ClassLoader loader = conn.getClass().getClassLoader();
Class<?>[] interfaces = conn.getClass().getInterfaces();
InvocationHandler h = new InvocationHandler() {
//对conn对象的方法进行重写
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断conn对象执行的方法是不是close方法,如果是,则将连接返回到连接池,而不是关闭连接
if("close".equals(method.getName())){
pools.addFirst(conn);
System.out.println("关闭后连接池有:"+pools.size());
return null;
}else{
return method.invoke(conn, args);
}
}
};
Connection proxyConn = (Connection) Proxy.newProxyInstance(loader, interfaces, h);
return proxyConn;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@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 int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
4.7 测试Demo完整代码
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestDemo {
public static void main(String[] args) {
ResultSet resultSet=null;
PreparedStatement statement=null;
Connection conn=null;
try {
//对象创建好之后,两件事已经完成:1.注册驱动已完成 2.拥有5个连接的连接池已经创建完成
MyDataSource myDataSource = new MyDataSource();
//从连接池中获取连接
conn = myDataSource.getConnection();
String sql="select * from emp";
statement = conn.prepareStatement(sql);
resultSet = statement.executeQuery();
while(resultSet.next()){
String id = resultSet.getString("id");
String name = resultSet.getString("name");
String city = resultSet.getString("city");
System.out.println(id+"="+name+"="+city);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
resultSet.close();
statement.close();
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
五、文件目录
六、测试结果
源码及配置文件:https://download.csdn.net/download/u010452388/10394985,有问题可以给我留言哈