【转】class loader 2 - ContextClassloader || talked about class.forName()/class.for

通过thread.getContextClassloader:

thread.getContextClassloader默认返回AppClassLoader,除非你显式setContextClassloader

 

来看一下jdbc中如何使用classloader:

一般我们写一个jdbc程序都会这样:

Class.forName("com.mysql.jdbc.Driver");

Stringurl ="jdbc:mysql://127.0.0.1/test?useUnicode=true&characterEncoding=utf-8";

Stringuser = "root";

Stringpsw = "yanyan";

Connectioncon = DriverManager.getConnection(url,user, psw);

为什么需要第一句话?

其实第一句话可以用下面这句话替代:

com.mysql.jdbc.Driverdriver = new com.mysql.jdbc.Driver();

其他都不用变化,有人会问,driver对象从来没有用到.,它的效果就是在调用DriverManagergetConnection方法之前,保证相应的Driver类已经被加载到jvm中,并且完成了类的初始化工作就行了.注意了,如果我们进行如下操作,程序是不能正常运行的,因为这样仅仅使Driver类被装载到jvm中,却没有进行相应的初始化工作。

com.mysql.jdbc.Driverdriver = null;

//or

ClassLoadercl = new ClassLoader();

cl.loadClass("com.mysql.jdbc.Driver");

 

我们都知道JDBC是使用Bridge模式进行设计的,DriverManager就是其中的Abstractionjava.sql.DriverImplementorcom.mysql.jdbc.DriverImplementor的一个具体实现(请参考GOFBridge模式的描述)。大家注意了,前一个Driver是一个接口,后者却是一个类,它实现了前面的Driver接口。

Bridge模式中,AbstractionDriverManager)是要拥有一个ImplementorDriver)的引用的,但是我们在使用过程中,并没有将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

//Class.forName("");的作用是要求JVM查找并加载指定的类,如果在类中有静态初始化器的话,JVM会      // 执行该类的静态代码段。而在JDBC规范中明确要求这个Driver类必须向DriverManager注册自己,//即任何一个JDBC Driver的 Driver类的代码都必须类似如下:

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类的类加载器为空的话,就使用当前线程的类加载器。仔细想想,DriverManagerrt.jar包中,它是由JDK的启动类加载器加载的,而启动类加载器是C编写的,所以取得的都是空,再者,使用当前线程类加载器的话,那么交由程序编写者来保证能够加载驱动类。而不至于驱动器类无法加载。非常高明的手段~! 

 

 

Class的这种设计引入了一个有趣的模式:

某个框架制定某个API,而这些api的实现是有其他供应商来提供,为了能让框架类(处于较高层次的classloader)使用api的实现(处于较低层次的classloader)

通过thread.getContextClassloader是来传递classloader(有时候需要thread.setContextClassloader设置好api实现的classloader,用此classloader.getResources找出所有的api实现的具体类名,再用classloader加载之,此时框架都不需要知道api的实现类的类名就能加载之,程序显示了良好的动态性和可扩展性。

 

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

 Class.forName("")返回的是类 

1. 总是使用当前装载器(也就是装载执行forName()请求类的类装载器)

2. 总是初始化这个被装载类(当然包括:装载,连接,初始化)

Class.forName("").newInstance()返回的是object

 

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

Class.forName(String className)
    Returns the Class object associated with the class or interface with the given string name

Class.forName(String name, boolean initialize, ClassLoader loader)

    Returns the Class object associated with the class or interface with the given string name, using the given class loader

 

Class.forName() 与 new 关键字的不同

  new 关键字适合用于对class文件在app ClassLoader 里,直接加载

  Class.forName()

    1. 经典用法是在工厂模式,

String classname = "Example";

Class c = Class.forName(classname);

factory = (ExampleInterface)c.newInstance();

classname 可以通过改变赋值,使生成的factory可以是任意继承ExampleInterface的类

   2. 经典的使用环境

上面提到的JDBC 的例子,为了适应不同厂商提供的JDBC Driver, 所以使用Class.forName 动态加载 
   3. 为什么需要Class.forName(), 很多时候,如果把所有jar一次性加载到JVM里, JVM寻找加载类的时间越过长, 所以需要类加载器去动态加载所需要的类。

 

为什么需要ContextClassLoader (上下文类加载器)

    动态加载类包和其他资源。 ContextClassLoader不是具体某一种 ClassLoader, 更像是一种链表的根。 如果class A 里加载了class B, A 通过 App ClassLoader加载, ContextClassLoader 会给 提供App ClassLoader 同样去加载 B.

    在多线程环境下, 也许一个线程里有几个类,而且并不是都是动态联编, 需要用当前的线程统一设置一个ContextClassLoader

应用的场景

  当前类上下文加载

    Class.getResource,  Class.forName();

    Java.util.ResourceBundle

    Java 序列化API默认调用者

  当前线程上下文加载

    JNDI

    JAXP

 

  URL protocol handlers specified via java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only

 

你可能感兴趣的:(Class.forName)