其实Java程序与MySQL数据库连接时,创建连接对象是最消耗资源的
然后而每次使用完就关闭然后需要的时候又开启,就会非常消耗内存资源
所以可以将不用的连接对象存放在一个容器中,而不是真正的关闭释放
连接池是创建和管理数据库连接的缓冲池技术
连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的
创建一个自定义连接池对象:
1.类实现DataSource接口,并重写DataSource所有抽象方法
2.实现类中要定义initCount(初始化连接个数),maxCount(最大存放连接个数),curCount(当前有连接个数)
3.定义一个LinkedList集合用于存放连接池中可用的连接
4.给连接池对象的构造方法体中,每次创建连接池对象,自动创建initCount个连接放入LinkedList集合中
5.定义一个创建连接的方法,每创建一个连接对象就要curCount++
6.关闭连接对象,不是真正的关闭连接而是将连接对象放回连接池中
public class ConnectionPool implements DataSource {
private int initCount = 3; //初始化连接池时,连接个数
private int maxCount = 10; //连接池最大容纳的连接数
private int curCount = 0; //当前连接池拥有连接数
private LinkedList<Connection> list = new LinkedList<>();
public ConnectionPool() {
for (int i = 0; i < initCount; i++) {
Connection conn = createConnection();
list.add(conn);
}
}
public Connection createConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/webdb2";
String user = "root";
String pwd = "abcd";
Connection conn = DriverManager.getConnection(url, user, pwd);
curCount++;
return conn;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Connection getConnection() throws SQLException {
if (list.size() > 0) {
Connection conn = list.removeFirst();
return conn;
}
if (curCount < maxCount) {
Connection conn = createConnection();
return conn;
}
throw new RuntimeException("连接已经使用达到上限");
}
public void close(Connection conn) {
list.add(conn);
}
}
至此,自定义的连接池就创建好了,要使用连接池中连接对象,可以直接调用getConnection()方法获取即可
我将部分没有使用到的重写方法省略了,自行创建时切记重写所有方法
动态代理:
为某个类的方法的进行增强
三要素:代理对象,被代理对象(就是要增强方法的那个对象),提供方法的接口
创建代理对象:
static object newProxyInstance(classLoader loader , Class[ ] interfaces , InvocationHanlder h )
loader:目标对象的类加载器,其实使用 本类.class.getClassLoader就可以获取
interfaces:要被代理对象的那个方法接口的class对象
InvocationHanlder:处理器,代理对象的具体操作都由处理器决定
创建处理器对象:
object invoke (Object proxy , Method method , Object [] args)
proxy:就是newProxyInstance()方法返回的代理对象
method:代理对象调用的方法对象
args:代理对象调用方法时传入的参数
例:想对连接池中的Connection的close()方法进行增强,让他调用close方法时不是直接关闭资源,而是放回连接池
在这个例子中,三要素中被代理对象就是原来连接池中的连接对象
代理对象就是我刚创建的connectionProxy对象
提供方法的接口就是Connection接口
public Connection createConnection() throws Exception{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/webdb2";
String user = "root";
String pwd = "abcd";
Connection conn = DriverManager.getConnection(url, user, pwd);
Connection connectionProxy = (Connection) Proxy.newProxyInstance(ConnectionPool.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (method.getName().equals("close")) {
list.add((Connection) proxy);
return null;
} else {
return method.invoke(conn);
}
}
});
curCount++;
return connectionProxy;
}
我将添加入连接池的所有连接对象都换成了代理对象
这些代理对象在调用close()方法时会将线程放回连接池,而不是直接关闭释放
其他方法仍是原来Connection所使用的方法不变
创建代理对象时,传入的本类的类加载器和接口参数,等同于得到的代理对象可以向上转型,用接口来接受对象
则代理对象就可以调用接口中的方法,且每次调用方法时候,都会通过Hanlder处理器
其实没有传入Hanlder处理器参数,代理对象运行接口中的方法也可以,只是方法体的内容都是空的
在处理器对象内,一定要有一个明确的被代理对象,例子中明确的代理对象就是conn
然后再用method.getName()获取方法名,当方法名与要增强的方法相同时,自己改写方法执行的语句(其实就是代理对象运行该方法,可是方法体和参数已经不是被代理对象的方法体和参数了)
若是不需要改写的方法,则使用反射中method.invoke()使用代理对象和代理对象传入的参数args,调用方法(其实就是代理对象运行该方法,但是和被代理对象运行是一样的方法体和一样的参数)
市面上其实已经有已经很成熟的连接池框架供我们使用
c3p0连接池:
使用c3p0先导入jar包,再配置自己欲得到的连接池信息即可
C3P0地址:https://sourceforge.net/projects/c3p0/?source=navbar
C3P0的jar包: c3p0-0.9.1.2.jar
参数 | 说明 |
initialPoolSize | 初始化连接数 |
maxPoolSize | 最大连接数 |
checkoutTimeout | 最大等待时间 |
maxIdleTime | 最大空闲回收时间 |
配置文件:c3p0-config.xml 统一放在源代码src根目录下
<?xml version="1.0" encoding="utf-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/webdb2</property>
<property name="user">root</property>
<property name="password">abcd</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">7</property>
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/webdb2</property>
<property name="user">root</property>
<property name="password">abcd</property>
<property name="initialPoolSize">3</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</named-config>
</c3p0-config>
public ComboPooledDataSource() 无参构造使用默认配置
ComboPooledDataSource cpds = new ComboPooledDataSource();
(使用xml中default‐config标签中对应的参数)
public ComboPooledDataSource(String configName) 有参构造使用命名配置
ComboPooledDataSource cpds = new ComboPooledDataSource("otherc3p0");
(configName:xml中配置的名称,使用xml中named‐config标签中对应的参数)
public Connection getConnection() throws SQLException 从连接池中取出一个连接
ComboPooledDataSource cpds = new ComboPooledDataSource("otherc3p0");
Connection conn = cpds.getConnection();
c3p0中的close()已经被动态代理增强,所以直接调用Connection的close()方法即可将连接放回连接池中
DRUID连接池:
Druid地址:https://github.com/alibaba/druid
DRUID连接池使用的jar包: druid-1.0.9.jar
参数 | 说明 |
initialSize | 初始化建立连接的个数 |
maxActive | 连接池最多连接个数 |
maxWait | 最大等待时间,单位毫秒 |
DRUID连接的配置文件时一个Properties文件,同样是放在源代码的src根目录下
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/webdb2
username=root
password=abcd
initialSize=5
maxActive=10
maxWait=3000
maxIdle=6
minIdle=3
使用DRUID连接池,需要自己创建一个Properties对象来加载配置文件
InputStream in = Test.class.getResourceAsStream("/druid.properties");
Properties prop = new Properties();
prop.load(in);
DataSource ds = DruidDataSourceFactory.createDataSource(prop);
Connection conn = ds.getConnection();
conn.close();
使用本类的class类,调用getResourceAsStream()方法,传入参数加上"/"代表根目录
直接获取一个字节输入流对象,再创建properties对象加载该配置文件
就可以调用DataSource ds = DruidDataSourceFactory.createDataSource(prop) 创建连接池对象
同样Druid连接池的连接对象也是代理对象,直接调用close()方法也可以直接将连接对象放回连接池中