看了好多反射机制的东西,感觉如果一个类的某些方法被设为私有的时候我们可以使用反射机制去访问。但是为什么jdbc驱动也都是使用反射机制去访问,很费解
我想题主想问的是如下代码为何要使用反射:
try { Class.forName("com.mysql.jdbc.Driver") ; } catch(ClassNotFoundException e) { System.out.println("找不到驱动程序类 ,加载驱动失败!"); return; // or do something else }
多年前写JDBC时年少无知,没有想过这个问题,今天题主提出来,觉得这个问题很好。
首先,上面一段代码的主要作用,是在 运行期
以反射的方式来检查JDBC驱动的主类 com.mysql.jdbc.Driver
是否存在,若不存则表示运行环境中没有这个驱动,进入catch段。如果你确定一定以及肯定它会存在,可以直接写成
import com.mysql.jdbc.Driver;
效果基本是一样的(只是在编译期及运行期要都保证此类存在classpath中)
所以,以反射形式加载的一个好处是当驱动jar包不存在时,我们可以做更多的操作。(要知道,在很久很久以前,jdbc驱动一般都是放在运行环境的classpath中的,如 tomcat/lib
)
如果你是一个有追求的程序员,那么另外一个很重要的原因是解耦。
首先要明白JDBC是Java的一种规范,通俗一点说就是JDK在 java.sql.*
下提供了一系列的接口(interface),但没有提供任何实现(也就是类)。 所以任何人都可以在接口规范之下写自己的JDBC实现(如MySQL)。而若调用者也只调用接口上的方法(如我们),那么当未来有任何变更需要时(例如要从MySQL迁移至Oracle),则理论上不需要对代码做任何修改就能直接切换(可惜SQL语法没能统一规范)
这意味着什么?意味着你的代码中不应该引用任何与实现相关的东西,你的代码只知道 java.sql.*
,而不应该知道 com.mysql.*
或是 com.oracle.*
,以避免或减少未来切换数据源时对代码的变更。
注意,我们使用的所有其他API包括 Connection
/ Statement
/ ResultSet
等都是 java.sql.*
的东西,甚至 com.mysql.jdbc.Driver
类也是:
package com.mysql.jdbc; public class Driver ... implements java.sql.Driver { ... }
因此,直接 import com.mysql.jdbc.Driver;
违反了开闭原则(OCP,对扩展开放,对修改关闭)。(有人说我用反射也必须要修改代码呀,事实上你可以将类名字符串存储至.properties文件,和数据库用户名密码放在一起,就像Hibernate做的那样)
如果我可以保证JDBC驱动一定在classpath下,是不是可以不写这段反射代码,也不引用任何的Driver类?答案是否定的,请看下面这段代码源自 com.mysql.jdbc.Driver
:
package com.mysql.jdbc; public class Driver extends NonRegisteringDriver implements java.sql.Driver { // // Register ourselves with the DriverManager // static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } ... }
static代码块会在类加载时就被执行——也就是当我们执行 Class.forName("com.mysql.jdbc.Driver")
时(或 import com.mysql.jdbc.Driver
)
转自: https://segmentfault.com/q/1010000000315618