您的位置:首页 > 编程语言 > Java开发

Java中用动态代理实现标准的DataSource数据源连接池

2017-12-24 17:46 281 查看

首先简单谈谈为什么要用连接池?

大家知道,我们平常连接数据库的时候,首先需要获取到数据库的连接 Java中对应的是 Connection,建立获取数据库连接是比较消耗资源的,而且每次建立获取连接也比较浪费时间,可以试想,如果每次请求过来,需要访问数据库时,都去重新建立并获取新的连接,就会浪费大量的资源和时间,此时客户端的相应时间肯定会较长,这并不是我们想看到的。因此这时候我们就要想办法避免这种现象,所以这时候就可以用连接池来解决。

其实简单的说,连接池实现的主要目的就是,获取连接的时候从池中获取已经初始化好的连接,这样做就不用每次都建立新的连接,就能节省资源的消耗,也能提高效率。然后在用完连接以后,调用conn.close( ); 关闭连接的时候并不是真去把这个连接关闭掉,而是应该把这个连接放回到池中,下次请求过来了,可以继续使用。

Java中连接池具体的实现一般有两种方式,一种是用包装类来实现,另一种是用动态代理来实现

现在常见的开源的Java连接池有DBCP和C3P0等,其中DBCP就是上边说的利用包装类来实现的,而C3P0是动态代理实现的。

今天只简单的讲一下动态代理的实现,关于包装类的实现方法,有时间的话,我也会整理一下

下面我们简单讲一下动态代理的实现,用动态代理写一个简单的连接池,供新同学参考,高手请自动忽略


首先贴一下为这次例子我建的一个简单的学生表,数据库是自己电脑虚拟机Linux上装的MySql数据库



超级的简单的一个表,就不多解释了。下面开始进入正题...

第一步:我们需要写一个标准的数据源DataSource,Java中DataSource是一个标准的数据源接口

/**
*
* @author caoju
*
*/
public class MyDataSource implements DataSource{

private static LinkedList<Connection> pool = new LinkedList<Connection>();
private static final String name = "com.mysql.jdbc.Driver";
private static final String url = "jdbc:mysql://192.168.199.188:3306/cj";
private static final String user = "root";
private static final String password = "123456";

static{//利用静态代码块儿在类一加载的时候就初始化10个连接到池中
try {
Class.forName(name);
for(int i=0;i<10;i++){
Connection conn = DriverManager.getConnection(url, user, password);
pool.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public Connection getConnection() throws SQLException {
if(pool.size()>0){
final Connection conn = pool.remove();
//返回动态代理对象
return (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);
}
}
});
}else{
throw new RuntimeException("对不起,服务器忙...");
}
}

@Override
public PrintWriter getLogWriter() throws SQLException {
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;
}

}

返回动态代理对象需要注意几点:一,代理对象需要和被代理的对象用一个类加载器;二,代理对象需要和被代理的对象实现同样的接口;三,需要new InvocationHandler()

来处理对象中具体的方法

第二步:建一个实体类对象,用来接收封装查询出来的数据

public class Student implements Serializable{

private static final long serialVersionUID = -1672204456370247243L;

private String id;

private String name;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
这就是个普通的bean对象,很简单,没啥可说的。

第三步:写一个调用端来测试写的连接池

/**
*
* @author caoju
*
*/
public class Client {

public static void main(String[] args) {
DataSource ds = new MyDataSource();

Connection conn = null;
PreparedStatement ps = null;
try {
conn = ds.getConnection();
ps = conn.prepareStatement("select * from student");
ResultSet rs = ps.executeQuery();
List<Student> stuList = new ArrayList<Student>();
while(rs.next()){
Student student = new Student();
String id = rs.getString("id");
String name = rs.getString("name");
student.setId(id);
student.setName(name);
stuList.add(student);
}
for (Student student : stuList) {
System.out.println(student);
}

} catch (Exception e) {
4000
e.printStackTrace();
}finally{
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

}

}


可以打断点看一下,返回的 conn 对象已经是$Proxy0,说明返回的已经是代理的对象了,而不是conn自身,而在返回的代理的对象里,我们已经对Close方法做了处理,所以就简单的实现了调用close的时候,不是真正的关闭数据库连接资源,而是把使用完的连接资源还回池中,供别的请求使用或者下次继续使用。



最后运行结果:



好了,一个简单的连接池就实现了,虽然代码比较简陋,但是能讲清楚里边的原理就好啦,哈哈。

大家可以动手试一试,过程中需要导入MySQL的驱动包,不要忘记啦。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息