电商网站建设与运营方向就业前景,如何选择镇江网站建设,注册一家公司需要什么条件,网站开发 需求转载自 破坏双亲委派机制的那些事
前言
今天重读《深入理解Java虚拟》这本书#xff0c;读到破坏双亲委派机制这一小节#xff0c;其中有一段话#xff0c;如下 双亲委派模型的第二次“被破坏”是由这个模型自身的缺陷所导致的#xff0c;双亲委派很好地解决了各个类加…转载自 破坏双亲委派机制的那些事
前言
今天重读《深入理解Java虚拟》这本书读到破坏双亲委派机制这一小节其中有一段话如下 双亲委派模型的第二次“被破坏”是由这个模型自身的缺陷所导致的双亲委派很好地解决了各个类加载器的基础类的统一问题越基础的类由越上层的加载器进行加载基础类之所以称为“基础”是因为它们总是作为被用户代码调用的API但世事往往没有绝对的完美如果基础类又要调用回用户的代码那该怎么办 这并非是不可能的事情一个典型的例子便是JNDI服务JNDI现在已经是Java的标准服务它的代码由启动类加载器去加载在JDK 1.3时放进去的rt.jar但JNDI的目的就是对资源进行集中管理和查找它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者SPI,Service Provider Interface的代码但启动类加载器不可能“认识”这些代码啊那该怎么办 为了解决这个问题Java设计团队只好引入了一个不太优雅的设计线程上下文类加载器Thread Context ClassLoader。这个类加载器可以通过java.lang.Thread类的setContextClassLoaser方法进行设置如果创建线程时还未设置它将会从父线程中继承一个如果在应用程序的全局范围内都没有设置过的话那这个类加载器默认就是应用程序类加载器。 有了线程上下文类加载器就可以做一些“舞弊”的事情了JNDI服务使用这个线程上下文类加载器去加载所需要的SPI代码也就是父类加载器请求子类加载器去完成类加载的动作这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器实际上已经违背了双亲委派模型的一般性原则但这也是无可奈何的事情。Java中所有涉及SPI的加载动作基本上都采用这种方式例如JNDI、JDBC、JCE、JAXB和JBI等。 一直已来对破坏双亲委派机制的理解也仅限于此今日再看到这段话对诱惑线程上下文类加载器如何破坏双亲委派不甚疑惑故查找网上资料结合自身理解作此文以记录。
JDBC中的逆双亲委派机制源码分析
感谢 真正理解线程上下文类加载器多案例分析的启发。 首先我们看一下JDBC连接数据库中加载驱动并获取连接的代码。 try{ //加载MySql的驱动类 Class.forName(com.mysql.jdbc.Driver) ; }catch(ClassNotFoundException e){ System.out.println(找不到驱动程序类 加载驱动失败); e.printStackTrace() ; } String url jdbc:mysql://localhost:3306/test ; String username root ; String password root ; try{ Connection con DriverManager.getConnection(url , username , password ) ; }catch(SQLException se){ System.out.println(数据库连接失败); se.printStackTrace() ; } 以上就是JDBC连接数据并获取连接的代码那调用这些的方法到底做了些什么呢 首先我们用Class.forName(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 var1) {throw new RuntimeException(Cant register driver!);}}
}我们用系统类加载器加载了com.mysql.jdbc.Driver类初始化的时候执行静态代码块静态代码块中将new了一个Driver实例并将他注册到DriverManager中。注意这里的Driver实例的类加载器是系统类加载器。接下来我们调用了DriverManager.getConnection(String url, String user, String password)其源码如下 CallerSensitivepublic static Connection getConnection(String url,String user, String password) throws SQLException {java.util.Properties info new java.util.Properties();if (user ! null) {info.put(user, user);}if (password ! null) {info.put(password, password);}return (getConnection(url, info, Reflection.getCallerClass()));}其调用了另一段关键代码如下
private static Connection getConnection(String url, java.util.Properties info, Class? caller) throws SQLException {/** 这里要确保类加载不能是BootstrapClassLoader* 因为BootstrapClassLoader不能加载到用户类库(JDBC驱动为用户类库)*/ClassLoader callerCL caller ! null ? caller.getClassLoader() : null;synchronized(DriverManager.class) {// synchronize loading of the correct classloader.if (callerCL null) {//获取系统类加载器callerCL Thread.currentThread().getContextClassLoader();}}if(url null) {throw new SQLException(The url cannot be null, 08001);}println(DriverManager.getConnection(\ url \));// Walk through the loaded registeredDrivers attempting to make a connection.// Remember the first exception that gets raised so we can reraise it.SQLException reason null;for(DriverInfo aDriver : registeredDrivers) {// If the caller does not have permission to load the driver then// skip it.if(isDriverAllowed(aDriver.driver, callerCL)) {try {println( trying aDriver.driver.getClass().getName());Connection con aDriver.driver.connect(url, info);if (con ! null) {// Success!println(getConnection returning aDriver.driver.getClass().getName());return (con);}} catch (SQLException ex) {if (reason null) {reason ex;}}} else {println( skipping: aDriver.getClass().getName());}}// if we got here nobody could connect.if (reason ! null) {println(getConnection failed: reason);throw reason;}println(getConnection: no suitable driver found for url);throw new SQLException(No suitable driver found for url, 08001);}这段代码并没有使用ClassLoader private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {boolean result false;if(driver ! null) {Class? aClass null;try {aClass Class.forName(driver.getClass().getName(), true, classLoader);} catch (Exception ex) {result false;}//类加载器与类相同才能确定result ( aClass driver.getClass() ) ? true : false;}return result;}小结此处线程上下文类加载器的作用主要用于校验存放的driver是否和调用时的一致由此判断是否有权限获取连接。