看一下jdbc中如何使用classloader: java
通常咱们写一个jdbc程序都会这样: mysql
Class.forName("com.mysql.jdbc.Driver"); sql
Stringurl ="jdbc:mysql://127.0.0.1/test?useUnicode=true&characterEncoding=utf-8"; api
Stringuser = "root"; 框架
Stringpsw = "yanyan"; jvm
Connectioncon = DriverManager.getConnection(url,user, psw); ui
为何须要第一句话? url
其实第一句话能够用下面这句话替代: spa
com.mysql.jdbc.Driverdriver = new com.mysql.jdbc.Driver(); 线程
其余都不用变化,有人会问,driver对象历来没有用到.对,它的效果就是在调用DriverManager的getConnection方法以前,保证相应的Driver类已经被加载到jvm中,而且完成了类的初始化工做就好了.注意了,若是咱们进行以下操做,程序是不能正常运行的,由于这样仅仅使Driver类被装载到jvm中,却没有进行相应的初始化工做。
com.mysql.jdbc.Driverdriver = null;
//or:
ClassLoadercl = new ClassLoader();
cl.loadClass("com.mysql.jdbc.Driver");
咱们都知道JDBC是使用Bridge模式进行设计的,DriverManager就是其中的Abstraction,java.sql.Driver是Implementor,com.mysql.jdbc.Driver是Implementor的一个具体实现(请参考GOF的Bridge模式的描述)。你们注意了,前一个Driver是一个接口,后者倒是一个类,它实现了前面的Driver接口。
Bridge模式中,Abstraction(DriverManager)是要拥有一个Implementor(Driver)的引用的,可是咱们在使用过程当中,并无将Driver对象注册到DriverManager中去啊,这是怎么回事呢?jdk文档对Driver的描述中有这么一句:
Whena Driver class is loaded, it should create an instance of itself andregister it with the DriverManager
哦,原来是com.mysql.jdbc.Driver在装载完后自动帮咱们完成了这一步骤。源代码是这样的:
packagecom.mysql.jdbc
publicclass Driver extends NonRegisteringDriver implements java.sql.Driver{
static{
try{
java.sql.DriverManager.registerDriver(newDriver());
}catch (SQLException E) {
thrownew RuntimeException("Can't register driver!");
}
}
publicDriver() throws SQLException {
//Required for Class.forName().newInstance()
}
}
再看一下DriverManager.getConnection(url,user, psw);方法:
ClassLoadercallerCL = DriverManager.getCallerClassLoader();
getConnection(url,info, callerCL)
==>
if(callerCL== null){
callerCL= Thread.currentThread().getContextClassLoader();
}
上面的意思是:代码意思是若是DriverManager类的类加载器为空的话,就使用当前线程的类加载器。仔细想一想,DriverManager在rt.jar包中,它是由JDK的启动类加载器加载的,而启动类加载器是C编写的,因此取得的都是空,再者,使用当前线程类加载器的话,那么交由程序编写者来保证可以加载驱动类。而不至于驱动器类没法加载。很是高明的手段~!
Class的这种设计引入了一个有趣的模式:
某个框架制定某个API,而这些api的实现是有其余供应商来提供,为了能让框架类(处于较高层次的classloader)使用api的实现(处于较低层次的classloader)
经过thread.getContextClassloader是来传递classloader(有时候须要thread.setContextClassloader设置好api实现的classloader),用此classloader.getResources找出全部的api实现的具体类名,再用classloader加载之,此时框架都不须要知道api的实现类的类名就能加载之,程序显示了良好的动态性和可扩展性。