最近学习了一下代理发现,代理其实一个蛮有用的,主要是用在动态的实现接口中的某一个方法而不去继承这个接口所用的一种技巧,首先是自定义的一个连接池
代码如下
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; /** * 自定义连接池, 管理连接 * 代码实现: 1. MyPool.java 连接池类, 2. 指定全局参数: 初始化数目、最大连接数、当前连接、 连接池集合 3. 构造函数:循环创建3个连接 4. 写一个创建连接的方法 5. 获取连接 ------> 判断: 池中有连接, 直接拿 ------> 池中没有连接, ------> 判断,是否达到最大连接数; 达到,抛出异常;没有达到最大连接数, 创建新的连接 6. 释放连接 -------> 连接放回集合中(..) * */ public class TestMyPool { private int init_count = 3; // 初始化连接数目 private int max_count = 6; // 最大连接数 private int current_count = 0; // 记录当前使用连接数 // 连接池 (存放所有的初始化连接) private LinkedList<Connection> pool = new LinkedList<Connection>(); //1. 构造函数中,初始化连接放入连接池 public TestMyPool() { // 初始化连接 for (int i=0; i<init_count; i++){ // 记录当前连接数目 current_count++; // 创建原始的连接对象 Connection con = createConnection(); // 把连接加入连接池 pool.addLast(con); } } //2. 创建一个新的连接的方法 private Connection createConnection(){ try { Class.forName("com.mysql.jdbc.Driver"); // 原始的目标对象 final Connection con = DriverManager.getConnection("jdbc:mysql:///jdbc_demo", "root", "root"); /**********对con对象代理**************/ // 对con创建其代理对象 Connection proxy = (Connection) Proxy.newProxyInstance( con.getClass().getClassLoader(), // 类加载器 //con.getClass().getInterfaces(), // 当目标对象是一个具体的类的时候 new Class[]{Connection.class}, // 目标对象实现的接口 new InvocationHandler() { // 当调用con对象方法的时候, 自动触发事务处理器 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 方法返回值 Object result = null; // 当前执行的方法的方法名 String methodName = method.getName(); // 判断当执行了close方法的时候,把连接放入连接池 if ("close".equals(methodName)) { System.out.println("begin:当前执行close方法开始!"); // 连接放入连接池 (判断..) pool.addLast(con); System.out.println("end: 当前连接已经放入连接池了!"); } else { // 调用目标对象方法 result = method.invoke(con, args); } return result; } } ); return proxy; } catch (Exception e) { throw new RuntimeException(e); } } //3. 获取连接 public Connection getConnection(){ // 3.1 判断连接池中是否有连接, 如果有连接,就直接从连接池取出 if (pool.size() > 0){ return pool.removeFirst(); } // 3.2 连接池中没有连接: 判断,如果没有达到最大连接数,创建; if (current_count < max_count) { // 记录当前使用的连接数 current_count++; // 创建连接 return createConnection(); } // 3.3 如果当前已经达到最大连接数,抛出异常 throw new RuntimeException("当前连接已经达到最大连接数目 !"); } //4. 释放连接 public void realeaseConnection(Connection con) { // 4.1 判断: 池的数目如果小于初始化连接,就放入池中 if (pool.size() < init_count){ pool.addLast(con); } else { try { // 4.2 关闭 current_count--; con.close(); } catch (SQLException e) { throw new RuntimeException(e); } } } public static void main(String[] args) throws SQLException { TestMyPool pool = new TestMyPool(); System.out.println("当前连接: " + pool.current_count); // 3 // 使用连接 pool.getConnection(); pool.getConnection(); Connection con4 = pool.getConnection(); Connection con3 = pool.getConnection(); Connection con2 = pool.getConnection(); Connection con1 = pool.getConnection(); // 释放连接, 连接放回连接池 // pool.realeaseConnection(con1); /* * 希望:当关闭连接的时候,要把连接放入连接池!【当调用Connection接口的close方法时候,希望触发pool.addLast(con);操作】 * 把连接放入连接池 * 解决1:实现Connection接口,重写close方法 * 解决2:动态代理 */ con1.close(); // 再获取 pool.getConnection(); System.out.println("连接池:" + pool.pool.size()); // 0 System.out.println("当前连接: " + pool.current_count); // 3 } }
在这里使用代理主要是为了监测Connection 中的close()方法,当然也可以检测Connection中的其他方法,顺便值得一提的是还有Jdbc的两个开源连接数据库的框架可以为连接数据库省去一些代码:例如dbcp和c3p0这两个框架实现代码分别如下,但前提必须将对应的jar包导入
import java.io.InputStream; import java.sql.Connection; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; import org.junit.Test; public class App_Dbcp { public App_Dbcp() { // TODO Auto-generated constructor stub } /** * 硬编码方式 */ @Test public void testDbcp() { //DBCP中的核心类 BasicDataSource dataSource = new BasicDataSource(); //连接池配置参数,初始化连接参数,最大连接参数,连接的驱动名 dataSource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); dataSource.setUrl("jdbc:sqlserver://localhost:1433;DatabaseName=教学库"); dataSource.setUsername("sa"); dataSource.setPassword("****"); dataSource.setInitialSize(3);//设置初始化连接参数 dataSource.setMaxActive(6);//设置最大连接数 dataSource.setMaxIdle(3000);//设置最大空闲时间 //获取连接 try { Connection con = dataSource.getConnection(); con.prepareStatement("delete from tb_user where id=3").executeUpdate(); //关闭 con.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e); } } @Test public void testDBCP() { /** * 使用配置方式来获取参数 */ try { Properties props = new Properties(); InputStream in = App_Dbcp.class.getResourceAsStream("/db.properties"); //读取流文件 props.load(in); //根据Props配置文件直接产生数据对象 DataSource dataSource = BasicDataSourceFactory.createDataSource(props); //创建连接对象 Connection con = dataSource.getConnection(); con.prepareStatement("delete from tb_user where id=4").executeUpdate(); con.close(); }catch(Exception e) { throw new RuntimeException(e); } } }
c3p0连接池的测试代码
package gz.itcast.b_c3p0; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; import org.junit.Test; import com.mchange.v2.c3p0.ComboPooledDataSource; public class App { public App() { // TODO Auto-generated constructor stub } //硬编码方式,使用c3p0连接池来管理连接数目 @Test public void testCode() throws Exception { //创建c3p0核心类 ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass("com.microsoft.sqlserver.jdbc.SQLServerDriver"); dataSource.setJdbcUrl("jdbc:sqlserver://localhost:1433;DatabaseName=教学库"); dataSource.setUser("sa"); dataSource.setPassword("***"); dataSource.setInitialPoolSize(3); dataSource.setMaxPoolSize(6); dataSource.setMaxIdleTime(3000); Connection con = dataSource.getConnection(); con.prepareStatement("delete from tb_user where id=3").executeUpdate(); con.close(); } //c3p0使用xml配置文件来管理连接方式 @Test public void testXml() throws Exception { //c3p0核心类,在new 一个对象时就会自动的加载c3p0-config.xml文件 ComboPooledDataSource dataSource = new ComboPooledDataSource(); Connection con = dataSource.getConnection(); con.prepareStatement("delete from tb_user where id=2").executeUpdate(); //关闭连接 con.close(); } }
对应的xml配置文件
<c3p0-config> <default-config> <property name="driverClass">com.microsoft.sqlserver.jdbc.SQLServerDriver</property> <property name="url">jdbc:sqlserver://localhost:1433;DatabaseName=教学库</property> <property name="username">sa</property> <property name="password">****</property> <property name="initialPoolSize">3</property> <property name="maxPoolSize">6</property> <property name="maxIdleTime">3000</property> <!-- <user-overrides user="swaldman"> <property name="debugUnreturnedConnectionStackTraces">true</property> </user-overrides> --> </default-config> <!-- <named-config name="dumbTestConfig"> <property name="maxStatements">200</property> <property name="jdbcUrl">jdbc:test</property> <user-overrides user="poop"> <property name="maxStatements">300</property> </user-overrides> </named-config> --> </c3p0-config>
最后就到为止了