JDBC为什么破坏双亲委派机制
JDBC的核心DriverManager在rt.jar中由启动类加载器加载,而其实现则在各厂商实现的的jar包中<br>根据类加载机制,若A类调用B类,则B类由A类的加载器加载,也就是说启动类加载器要加载jar包下的类,<br>我们都知道这是不可能的,启动类加载器负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,那么JDBC是如何加载这些Driver实现类的?<br>
通过Thread.currentThread().getContextClassLoader()得到线程上下文加载器来加载Driver实现类
父ClassLoader可以使用当前线程Thread.current.currentThread().getContextClassLoader()所指定的classLoader加载的类。<br>这就改变了父ClassLoader不能使用子ClassLoader加载的类的情况,即改变了双亲委托模型<br>
加载驱动的两种方式<br>在加载某一 Driver 类时,它应该创建自己的实例并向 DriverManager 注册该实例<br>
Driver driver = new Driver();//com.mysql.jdbc.Driver<br>DriverManager.registerDriver(driver);<br>
手动注册,内部也执行静态代码块,这相当于实例化了两个Driver对象
会产生对某一种数据库的依赖(会import驱动包),耦合性较高
Class.forName("com.mysql.jdbc.Driver");<br>
当执行 Class.forName(driverClass) 获取其Class对象时, com.mysql.jdbc.Driver 就会被JVM加载,<br>连接,并进行初始化,初始化就会执行静态代码块java.sql.DriverManager.registerDriver(new Driver());<br>
Mysql的spi加载机制,通过获取当前线程的类加载器加载了SPI配置的驱动,<br>com.mysql.jdbc.Driver被初始化,注册驱动<br>
tomcat类加载器为什么要破坏双亲委派机制?
tomcat是web容器,需要解决的问题
1.一个web容器可能要部署两个或者多个应用程序,不同的应用程序,可能会依赖同一个第三方类库的不同版本,<br>因此要保证每一个应用程序的类库都是独立、相互隔离的。<br>
2. 部署在同一个web容器中的相同类库的相同版本可以共享,否则,会有重复的类库被加载进JVM
3. web容器也有自己的类库,不能和应用程序的类库混淆,需要相互隔离
4. web容器支持jsp文件修改后不用重启,jsp文件也是要编译成.class文件的,支持HotSwap功能
如果使用双亲委派的问题?
1. 默认的类加载器无法加载两个相同类库的不同版本,它只在乎类的全限定类名,<br>并且只有一份,所以无法解决上面1和3,相互隔离的问题<br>
2. 修改jsp文件后,因为类名一样,默认的类加载器不会重新加载,而是使用方法区中已经存在的类;<br>所以需要每个jsp对应一个唯一的类加载器,当修改jsp的时候,直接卸载唯一的类加载器,然后重新创建类加载器,并加载jsp文件<br>
tomcat的类加载器
CommonClassLoader<br>
tomcat最基本的类加载器,加载路径中的class可以被tomcat和各个webapp访问<br>
CatalinaClassLoader<br>
tomcat私有的类加载器,webapp不能访问其加载路径下的class,即对webapp不可见
SharedClassLoader
各个webapp共享的类加载器,对tomcat不可见
WebappClassLoader
webapp私有的类加载器,只对当前webapp可见
JspClassLoader
每个jsp一个类加载器
每一个web应用程序对应一个WebappClassLoader,每一个jsp文件对应一个JspClassLoader,所以这两个类加载器有多个实例
工作原理
a. CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使用,从而实现了公有类库的共用<br>
b. CatalinaClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离<br>
c. WebAppClassLoader可以使用SharedClassLoader加载到的类,<br>但各个WebAppClassLoader实例之间相互隔离,多个WebAppClassLoader是同级关系<br>
d. 而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:<br>当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能<br>
tomcat目录结构
/common/*<br>/server/*<br>/shared/*<br>/WEB-INF/*
默认情况下,conf目录下的catalina.properties文件,没有指定server.loader以及shared.loader,所以tomcat没有建立CatalinaClassLoader和SharedClassLoader的实例,这两个都会使用CommonClassLoader来代替。Tomcat6之后,把common、shared、server目录合成了一个lib目录。所以在我们的服务器里看不到common、shared、server目录
每个webappClassLoader加载自己目录下的class文件<br>每个jasper类加载器加载一个jsp文件<br>
加载过程
1.先在本地缓存中查找是否已经加载过该类(对于一些已经加载了的类,<br>会被缓存在resourceEntries这个数据结构中),如果已经加载即返回,否则 继续下一步。<br>
2.让系统类加载器(AppClassLoader)尝试加载该类,主要是为了防止一些基础类会被web中的类覆盖,<br>如果加载到即返回,返回继续。<br>
3.前两步均没加载到目标类,那么web应用的类加载器将自行加载,如果加载到则返回,否则继续下一步。
4.最后还是加载不到的话,则委托父类加载器(Common ClassLoader)去加载。<br>
双亲委派模型过程
分类
启动(Bootstrap)类加载器
负责将 Java_Home/lib下面的类库加载到内存中(比如rt.jar)
标准扩展(Extension)类加载器
负责将Java_Home /lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中
应用程序(Application)类加载器
负责将系统类路径(CLASSPATH)中指定的类库加载到内存中
由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般称为系统(System)加载器
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,<br>如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载<br>