收录日期:2021/01/18 22:23:00 时间:2016/07/18 05:54:09 标签:Java SE

/**
 * 单例模式,通过DBPool连接池获取数据库连接
 * @author shi
 */
public class JdbcUtils {

private static Logger logger = Logger.getLogger(JdbcUtils.class);

//简单的数据库连接池
private static DataSource dataSource = new DBPool();
private static JdbcUtils instance; 

//构造函数私有化,为实现单例
private JdbcUtils() {
}

//注意:"锁"的方式,以及第二次"null"的判断(仔细思考:若两个线程同时到达)
public static JdbcUtils getInstance() {
if(instance == null) {
synchronized (JdbcUtils.class) {
if(instance == null) {
instance = new JdbcUtils();
}
}
}
logger.info("成功获取JdbcUtils实例");
return instance;
}

//返回连接Connection
public Connection getConnetion() throws SQLException {
return dataSource.getConnection();

}

//释放资源
public void free(ResultSet rs, Statement st, Connection conn) {
try {
if(rs != null) rs.close();
} catch (SQLException e) {
throw new JdbcException(e.getMessage());
} finally {
try {
if(st != null)st.close();
} catch (SQLException e) {
throw new JdbcException(e.getMessage());
} finally {
try {
if(conn != null) conn.close();
} catch (SQLException e) {
throw new JdbcException(e.getMessage());
}
}
}
}
}
----------------------------------------------------------------------------
/**
 * 数据库连接池
 * @author shi
 */
public class DBPool{
private static Logger logger = Logger.getLogger(DBPool.class);
private static LinkedList<Connection> connectionPool;
private static int currentCount;
private static int initCount;
private static int maxCount;
private static String sqlDriver;
private static String sqlUrl;
private static String database;
private static String user;
private static String password;
private static String encoding;
private static String url;

/*
 * 静态块初始配置
 */
static {
Properties pro = new Properties();
InputStream in = DBPool.class.getClassLoader().getResourceAsStream("db.properties");
try {
pro.load(in);
} catch (IOException e) {
logger.error("加载配置文件出现错误");
throw new RuntimeException(e.getMessage());
}

/*
 * 读取配置信息
 */
sqlDriver = pro.getProperty("sqlDriver");
database = pro.getProperty("database");
sqlUrl = pro.getProperty("sqlUrl");
user = pro.getProperty("user");
password = pro.getProperty("password");
encoding = pro.getProperty("encoding");
url = sqlUrl + database + "?user=" + user + "&password=" + password + "&" + encoding;
initCount = Integer.parseInt(pro.getProperty("initCount"));
maxCount = Integer.parseInt(pro.getProperty("maxCount"));
try {
Class.forName(sqlDriver);
} catch (ClassNotFoundException e) {
logger.error("加载驱动出现错误");
throw new RuntimeException(e.getMessage());
}
}
/*
 * 包访问权限的构造函数,尽量消除其它类对DBPool的依赖
 */
DBPool() {
connectionPool = new LinkedList<Connection>();
for(int i=0; i<initCount; i++) {
try {
Connection conn = DriverManager.getConnection(url);
// 动态代理
DynamicConnProxy dcp = new DynamicConnProxy(conn, this);
Connection c = dcp.bind();
connectionPool.addLast(c);
currentCount++;
} catch (SQLException e) {
logger.error("获取数据库连接出现错误");
throw new RuntimeException(e.getMessage());
}
}
}
/*
 * 获取数据库连接
 */
public Connection getConnection(){
synchronized (connectionPool) {
if(connectionPool.size() > 0) {
return connectionPool.removeFirst();
} else if(currentCount < maxCount) {
Connection c = null;
try {
DynamicConnProxy dcp = new DynamicConnProxy(DriverManager.getConnection(url), this);
c = dcp.bind();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage());
}
currentCount++;
return c;
} else {
throw new RuntimeException("数据库负载超荷");
}
}
}
/*
 * 关闭数据库连接
 */
public void close(Connection conn) {
connectionPool.addLast(conn);
}
}
----------------------------------------------------------------------------
/**
 * 动态代理的模式
 * @author shi
 */

public class DynamicConnProxy implements InvocationHandler {
private Connection warpedConn;
private Connection realConn;
private DBPool dbpool;
DynamicConnProxy(Connection conn, DBPool dbpool) {
this.realConn = conn;
this.dbpool = dbpool;
}
public Connection bind() {
this.warpedConn = (Connection) Proxy.newProxyInstance(
DynamicConnProxy.class.getClassLoader(),
new Class[] { Connection.class }, this);
return this.warpedConn;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

String methodName = method.getName();
if ("close".equals(methodName)) {
dbpool.close(warpedConn);
return null;
} else {
return method.invoke(realConn, args);
}
}
}



上面是利用动态代理的一个小例子。但“动态代理”的内部机制是怎样实现的呢?还是通过“反射”吗?请大家指点。
先占个沙发再说
反射肯定是要用到的
动态代理远远超过反射的能力范围,JDK 的动态代理应该是自动生成一个代理类。而 Cglib 框架的动态代理是使用 ASM 这个字节码工具,直接修改字节码而来的。

PS:你的单例方法有问题,JDK 中的双检锁无效,除非 JDK 1.5 以上版本在变量上修饰 volatile 时双检锁才有效果。
你这些代码,我看得稀里糊涂的

private static DataSource dataSource = new DBPool();

DBPool 并没有实现 DataSource 接口,可以这样用么?

    private static LinkedList<Connection> connectionPool;
    private static int currentCount;
    private static int initCount;
    private static int maxCount;
    private static String sqlDriver;
    private static String sqlUrl;
    private static String database;
    private static String user;
    private static String password;
    private static String encoding;
    private static String url;

还有这一堆的东西,也没看懂为什么要弄成 static 的?

要弄动态代理千万别拿连接池来做试验,连接池是个非常复杂的东西,并不是那么简单的,你的这个连接池只解决了一个 Connection 的 close 问题。

当用户获得不到连接池,应该使用 wait 方法,或者是 wait(timeout) 在那里等着,超时后再抛出异常,这样会比较好一些。

另外,你这里也没有 Connection 的健康状况检查,数据库服务器会把一些占用时间很长的连接主动断开,如果是这样的话,长时间使用会导致很多连接无效。

再者,如果网络连接断开,也没看到有将池中连接重新刷新的代码。
动态代理背后就是反射 没什么稀奇的
谁说的动态代理就是反射啊?
引用 6 楼 bao110908 的回复:
谁说的动态代理就是反射啊?

动态代理比反射复杂的多!
动态代理用到了反射,最关键的还是动态修改字节码
引用 6 楼 bao110908 的回复:
谁说的动态代理就是反射啊?


我说肯定用到了反射 没说只用了反射

动态代理的本质就在这个方法里
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces);
这个proxyName难道不是反射得到的吗?

然后通过这个名字和接口生成java源代码 编译成字节码文件 调入虚拟机 实例化 返回对象 

就这么简单
还有实例化的时候也是通过反射

我做了个测试 调用ProxyGenerator.generateProxyClass(proxyName, interfaces);方法得到字节码 写到文件中(.class文件) 然后通过JD反编译 得到代理类(.java文件)

这个类是这样的
public final class MyProxyClass extends Proxy implements MyInterface
有一个构造方法
public MyProxyClass(InvocationHandler paramInvocationHandler)
    throws {
    super(paramInvocationHandler);
  }



所以我们通常用动态代理是这么用的:

写一个类,比如名字叫MyHandler,实现InvocationHandler接口.
然后把MyHandler对象作为参数传给Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);这个方法.
这个方法会调用ProxyGenerator.generateProxyClass(proxyName, interfaces);方法生成一个代理类.然后通过反射调用上面那个构造方法进行实例化,参数就是自己写的MyHandler的对象.
而这个代理类的任何方法实际上都是调用这个MyHandler的invoke方法,只是不同方法的参数不同.

所以无论你调用什么方法,实际上都在调用你写的invoke方法.
这样你就可以在invoke方法里自由发挥,前插后插,随便你怎么插,这就是AOP.
你认为简单就简单吧,反正你也一口咬定了,我也无话可说了。

JDK 的动态代理有一定的局限性,被代理的类必须有个接口,否则无法进行代理。
应该每个人都要觉得简单才对.

无论学什么东西,做什么事情都是一样,想复杂了就怕,怕了就不敢去碰.

其实想宽点,人一辈子也就那么回事,没什么稀奇的,何况其他.

所以活着就要放肆一点,潇洒一点.
很强大
非常感谢“火龙果”和“haojia0716”的指点!

的确和“火龙果”说的那样存在的问题太多。希望几位能够把自己的理解分享一下。真诚感谢。

---------------------------

1.还有这一堆的东西,也没看懂为什么要弄成 static 的? 这个的确没必要。改正。

2.DBPool 并没有实现 DataSource 接口,可以这样用么?这是自己发帖的时候,修改了DBPool,另外一边

忘了改。 

像连接池的“错误机制”、“算法”等等问题,是更重要的。

-----------------------

自己目前的理解程度和haojia0716所做的测试一样。

希望火龙果能够给更多的指点!谢谢
 





引用 9 楼 haojia0716 的回复:
引用 6 楼 bao110908 的回复:
 谁说的动态代理就是反射啊?


 我说肯定用到了反射 没说只用了反射

 动态代理的本质就在这个方法里
 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                     proxyName, interfaces);
 这个proxyName难道不是反射得到的吗?

 然后通过这个名字和接口生成java源代码 编译成字节码文件 调入虚拟机 实例化 返回对象

 就这么简单


这里并没有生成 java 源代码,也没有什么编译之说,而是根据 JVM 规范直接生成 class 类的字节码,再通过 native 方法加载这个类。而且在非调试状态下连 .class 文件都不会生成的,除非 System Property 的 sun.misc.ProxyGenerator.saveGeneratedFiles 的值为 true 才会产生 .class 文件。

作为一个 Proxy 的代理类,其代理对象的类名为代理类第一个 public 接口的包名加上 $Proxy 再加上数字。

比如被代理对象第一个 public 接口为 net.csdn.j2se.Service 的话,那么代理对象的类名为 net.csdn.j2se.$Proxy0

这个代理对象会被 Proxy 缓存,如果多次使用 Proxy.newProxyInstance 进行同一个代理的话,只会产生一个 $Proxy0 的类名,如果还有其他代理的话会是 $Proxy1, $Proxy2 依次下去。

但是 JDK 的 Proxy 有一个局限,就是只能为接口创建代理对象,如果需要代理的某个类没有接口的话,那 JDK 将无计可施。这时我们会用到另一个动态代理的实现 Cglib,Cglib 采用 ASM 这个字节码框架,在运行时动态地为一个类创建一个代理子类,此时并不需要这个类有个接口,但由于是动态生成一个子类,因此类中的方法不能使用 final 进行修饰,否则无法创建代理对象。

JDK 的动态代理是 JDK 1.3 开始引入的功能,但是当初的性能是非常差的,虽然现在性能提高不少,但在某些方面还是逊色于 Cglib,但各有优缺点,据有些研究表明 JDK 代理在创建代理对象的性能要低于 Cglib 的代理,但是 Cglib 创建代理类的性能要低于 JDK 的动态代理。

根据一些最佳实践表示,如果代理对象不需要频繁的创建,比如说采用了单例模式时,可以使用 Cglib 的代理,反之应使用 JDK 的代理。

关于 JDK 与 Cglib 代理性能可以看一下 TheServiceSide 的这篇讨论:

http://www.theserverside.com/news/thread.tss?thread_id=45089


在 Rob Harrop, Jan Machacek 所编写的 Pro Spring 一书第 6 章 All About Proxies 一节中也有 JDK 与 Cglib 的性能分析。不过该性能测试是使用 JDK 1.4 进行的。JDK 5、JDK 6 起 JDK 的动态代理性能有了明显的提高。
我当时回完帖子后就想了想,应该就是直接生成个class.不过懒得去管他了.
cglib还是比较受欢迎点,hibernate懒加载也是用他.

天亮了,我要去睡觉了.
因此动态代理跟反射的关系并不是太大。

其动态生成的字节码中,会将需要代理的每一个方法作为代理对象的一个类型为 Method 的属性,便于动态代理对象进行调用。
no 我认为关系很大

"动态"二字就是基于反射的

若不用反射 只能叫静态代理了
jdk有jdk的反射 cglib有cglib的反射

广义的讲

反射也就是在编译时,你获取了一个指向某个在你的程序空间中并不存在的对象的引用;事实上在编译时你的程序无法获知这个对象所属的类.

所以说cglib背后的机制也可以叫反射.难道不同于jdk的那种方式就不能叫反射了?
争论好剧烈呀,偶不懂这些,只有听的份

反射是一种让框架能够根据 "以字符串形式存在的信息" 来调用对象的属性和函数的技术.
学习,收益匪浅.
 地方安德森发送速度
如何获取代理类的源码啊!

HP-UNIX小机oracle11g安装在哪里? .Net线程管理 这个程序有什么用? 请教个文件操作的问题 基础问题--php页面无输出 亲,又来难题了。 两个结构相同datatable进行合并 【怎么播放麦克风的声音】 想做MTK虚拟内存功能,程序怎么实现? &#开头的是什么编码 怎么转换成中文 悲催的孩子! Ext.form.formPanel 最近上了韩国的蔡妍,好可爱哦。 我和一个外国佬的对话,目前我还没明白他找我干什么 WPF DataGrid的表头样式 急~ PB数据累加 我想请问一下,读取简单的文档的小程序谁有啊?谢谢了 SAX能否修改XML文件,能否给个修改xml的例子(sax,或Dom)? wince6.0屏触摸问题 这个有问题吗 基情四角,双*#性,因妒成恨,情侣互害,尼玛狗血剧情逼得不吐不快!! 【Sky_大熊猫】找到一些经典的搞笑图片,与大家分享~ (不喜勿喷) android listview 自定义样式 JNI中读十六进制文件的问题 求asp.net实时更新的走势图 如何看源码啊 谁帮我写个小循环呀!! 求帮助、、、大家好、我想学习数据库oracle或sql的知识。没基础、应该从何学起、谢谢、、、 怎么消除使用Lay out in spilitter 说金钱是罪恶,都在捞;说美女是祸水,都想要;说高处不胜寒,都在爬;说烟酒伤身体,都不戒;说天堂最美好,都不去!