目录
1、Class.forName()有什么作用呢?
2、为什么不直接new?
3、为什么删除Class.forName("com.mysql.jdbc.Driver")还是可以运行?
JDBC是Bridge模式的典型应用,DriverManager就是其中的Abstraction;java.sql.Driver是Implementor,com.mysql.jdbc.Driver是Implementor的一个具体实现。前一个Driver是一个接口,后者却是一个类,它实现了前面的Driver接口。 Bridge模式中,Abstraction(DriverManager)是要拥有一个Implementor(Driver)的引用的。
一般,我们在连接数据库时,会使用如下的代码:
try {
Class.forName("com.mysql.jdbc.Driver");
connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
} catch (Exception e) {
e.printStackTrace();
}
1、Class.forName()有什么作用呢?
首先,我们都知道,在java中Class.forName()方法在java中的作用是1、用于在JVM中查找并加载指定的类到内存中。2、初始化这个类
此时,我们将"com.mysql.jdbc.Driver"作为参数传入,是让JVM到"com.mysql.jdbc"路径下,找到Driver类并将其加载到内存中。
我们一起来看一下Driver类的详细代码:
public class Driver extends NonRegisteringDriver
implements java.sql.Driver
{
public Driver() throws SQLException{
}
static
{
try
{
DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
}
Class.forName(“com.mysql.jdbc.Driver”)的作用实际上就是调用DriverManager的 registerDriver方法注册一个mysql的JDBC驱动(Driver)而已(简而言之,就是将mysql驱动注册到DriverManager中去)。
其中调用的DriverManager的 registerDriver方法中的关键代码有:
private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers =
new CopyOnWriteArrayList();
它的底层利用了一个CopyOnWriteArrayList作为容器(这是一个线程安全的容器,不过每次add的时候都会对底层数组进行一次新的复制,所以在读远多于写的时候建议可以使用这个),放那些注册进去的DriverInfo。最终getConnection(...)的时候就拿registerDrivers里面注册进去的具体的某个数据库的DriverInfo(像MySql的Driver就在DriverInfo里面)去连接具体的数据库。
整个流程就是:
JDK不负责和数据库连接打交道,也没必要,只提供一个具体的接口Driver,告诉所有第三方,要连接数据库,就去实现这个接口,然后通过DriverManager注册一下,到时候连接某个数据库的时候,你已经在我这里注册了,我会调用你注册进来的Driver里面的方法去对指定数据库进行连接的。然后Mysql就实现自己的Driver,Oracle就实现自己的Driver,通过static块注册一下,再然后,就没有然后了。
这样之后,我们就可以通过DriverManager的getConnection方 法获得mysql的连接了:
connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/test"
, "root", "root");
到此为止,我们就获得了connection对象,现在就可以对数据库进行操作了。
总而言之,我们只需要在调用DriverManager的getConnection方法之前,保证相应的Driver类已经被加载到jvm中,并且完成了类的初始化工作就行了,而具体是怎样实现这个功能却是没有讲究的。
2、为什么不直接new?
意思是这么写"com.mysql.jdbc.Driver d = new com.mysql.jdbc.Driver();",这样写也是可以的,因为在new的时候会自动触发对一个类的初始化。
但问题是new出来干嘛?com.mysql.jdbc.Driver里面的方法我们会用到吗,并且我们知道怎么用吗?仅仅为了初始化一个类而new一个类实例出来还不如不去new,直接使用Class.forName(String name)初始化就可以了。DriverManager类的getConnection(...)方法的存在本身就是帮助用户调用Driver里面的各种方法连接数据库,JDK都做好了,开发者就没必要自己写了。
特别要注意的是,如果我们进行如下操作,程序是不能正常运行的,因为这样仅仅使Driver类被装载到jvm中,却没有进行相应的初始化工作。
com.mysql.jdbc.Driver driver = null;
//or:
ClassLoader cl = new ClassLoader();
cl.loadClass("com.mysql.jdbc.Driver");
3、为什么删除Class.forName("com.mysql.jdbc.Driver")还是可以运行?
1996年1月23日JDK1.0发布,Java语言有了第一个正式版本的运行环境。JDBC是1997年2月19日,在JDK1.1的版本中发布的,从版本就看得出,JDBC属于Java技术的一些最基础的功能点。那在JDK1.5之后,其实已经不需要去显式调用Class.forName("com.mysql.jdbc.Driver")了,DriverManager会自动去加载合适的驱动,但是前提是CLASSPATH下必须有驱动jar包。
在DriverManager的源码中可以看到一个静态块:
/**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
进入loadInitialDrivers()方法中看到以下一段代码:
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;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
重点是这句代码:
ServiceLoader.load(Driver.class);
这行代码可以把类路径下所有jar包中META-INF/services/java.sql.Driver文件中定义的类加载上来,此类必须继承自java.sql.Driver。