問題拋出
jdbc只是接口端考,數據庫驅動是其實現图仓,是如何找到驅動并進行加載使用罐盔?類路徑有多個數據庫驅動實現的時候,該選擇哪個救崔?
jdbc模板示例
下面是jdbc接入的范例惶看,首先將數據庫驅動添加到工程中,其中加載驅動程序的代碼是可選的Class.forName("com.mysql.jdbc.Driver");
帚豪,可以不寫碳竟。那么問題來了,程序是如何找到驅動程序并完成加載呢狸臣?
public class DbUtil {
public static final String URL = "jdbc:mysql://localhost:3306/imooc";
public static final String USER = "liulx";
public static final String PASSWORD = "123456";
public static void main(String[] args) throws Exception {
//1.加載驅動程序
Class.forName("com.mysql.jdbc.Driver");
//2. 獲得數據庫連接
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
//3.操作數據庫,實現增刪改查
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT user_name, age FROM imooc_goddess");
//4. 如果有數據昌执,rs.next()返回true
while(rs.next()){
System.out.println(rs.getString("user_name")+" 年齡:"+rs.getInt("age"));
}
}
}
驅動類加載的時機
首先烛亦,通過DriverManager
找到數據庫驅動诈泼,并進行加載。
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
DriverManager
用來管理類路徑中的數據庫驅動煤禽。如何找到數據庫驅動類铐达,關鍵點在于,ServiceLoader
SPI檬果,加載Driver.class
瓮孙。這個loadInitialDrivers是DriverManager加載時觸發(fā)的。
具體代碼如下
public class DriverManager{
private DriverManager(){}
static {
loadInitialDrivers();//加載驅動
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 通過ServiceLoader加載Driver類
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
}
ServiceLoader
一般使用接口的實現類都是靜態(tài)new一個實現類賦值給接口引用选脊,如下:
HelloService service = new HelloImpl();
如果需要動態(tài)的獲取一個接口的實現類呢杭抠?全局掃描全部的Class,然后判斷是否實現了某個接口恳啥?代價太大偏灿,一般不會這么做。一種合適的方式就是使用配置文件钝的,把實現類名配置在某個地方翁垂,然后讀取這個配置文件,獲取實現類名硝桩。JDK提供的ServiceLoader就是這種方式沿猜。
具體用法,就是在實現類的jar包的META-INF下新建一個文件夾services碗脊,并在services下新建一個文件啼肩,以接口的全限定名為文件名,內容為實現類的全限定名望薄。
怎么找到驅動類疟游?
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
DriverManager加載的時候,會load Driver.class痕支,Driver.class的全限定名是java.sql.Driver
颁虐。
也就是通過ServiceLoader機制,完成了驅動類的加載
如何選擇驅動類卧须?
當類路徑有多個驅動類時另绩,通過配置來匹配驅動,完成驅動的初始化花嘶。
public static final String URL = "jdbc:mysql://localhost:3306/imooc";
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
url指定了采用的協議是jdbc:mysql
笋籽,