Java类加载器( 死磕9)
【正文】Java类加载器( CLassLoader ) 死磕9:
上下文加载器原理和案例
本小节目录
9.1. 父加载器不能访问子加载器的类
9.2. 一个宠物工厂接口
9.3. 一个宠物工厂管理类
9.4 APPClassLoader不能访问子加载器中的类
9.5. 线程上下文类加载器
类加载器的代理模式并不能解决 Java 应用开发中会遇到的类加载器的全部问题。Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers包中。这些 SPI 的实现代码很可能是作为 Java 应用所依赖的 jar 包被包含进来,可以通过类路径(CLASSPATH)来找到,如实现了 JAXP SPI 的 Apache Xerces所包含的 jar 包。SPI 接口中的代码经常需要加载具体的实现类。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory类中的 newInstance()方法用来生成一个新的 DocumentBuilderFactory的实例。这里的实例的真正的类是继承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的实现所提供的。如在 Apache Xerces 中,实现的类是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。而问题在于,SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的 Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题。
线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。
在类java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。
如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是AppClassLoader类加载器。
线程上下文类加载器正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。
1.1.1. 父加载器不能访问子加载器的类
关于父加载器,不能访问子加载器加载的类,是一个比较大的问题,举个栗子。
1.1.2. 一个宠物工厂接口
这里有一个宠物工厂接口:
face IPetFactory { /** * 根据类型,构造一个对象 * @param type 类型,如 Type.CAT * @return */ IPet buildePet(IPet.Type type); }
1.1.3. 一个宠物工厂管理类
还有一个宠物工厂管理类,根据类名,加载工厂实现类。
public class FactoryManager { /** * 单例的Pet实例工厂 */ private static IPetFactory petFactory; /** * 已经类名,取得工厂实例 * * @param factoryClassName * @return */ public static IPetFactory getInstance(String factoryClassName) { if (null != petFactory) { return petFactory; } if (null == factoryClassName) { //默认的实现 factoryClassName = "com.crazymakercircle.petStore.pet.factory.PetFactoryImpl"; } /** * 获得线程上下文加载器 */ ClassLoader loader = Thread.currentThread().getContextClassLoader(); try { // Class<?> factoryClass = loader.loadClass(factoryClassName); Class<?> factoryClass=Class.forName(factoryClassName); petFactory = (IPetFactory) factoryClass.newInstance(); } catch (Exception e) { e.printStackTrace(); } return petFactory; } }
1.1.4. APPClassLoader不能访问子加载器中的类
先上代码:
public class FailCase { private static IPetFactory petFactory=null; public static void testLoader() { try { String baseDir = SystemConfig.PET_LIB_PATH; FileClassLoader classLoader = new FileClassLoader(baseDir); String className = SystemConfig.PET_FACTORY_CLASS; Class petFactoryClass = classLoader.loadClass(className); Logger.info("显示petFactoryClass 的ClassLoader tree:"); ClassLoaderUtil.showLoader4Class(petFactoryClass); petFactory = FactoryManager.getInstance(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void showPet() { IPet dog=petFactory.buildePet(IPet.Type.DOG); dog.sayHello(); } public static void main(String[] args) { testLoader(); showPet(); } }
运行上面的栗子,试一试。
发现,是失败的。
原因是,FactoryManager是APPClassLoader加载的,而第三方的工厂类是FileClassLoader 加载的。
FileClassLoader 是APPClassLoader的子加载器。子加载器可以访问父加载器中的类,但是父亲不能访问子加载器中的类。
所以,FactoryManager没有办法load子加载器中的工厂实现类。
1.1.5. 线程上下文类加载器
使用线程上下文类加载器,可以在执行线程中抛弃双亲委派加载链模式,使用线程上下文里的类加载器加载类。
public class CorrectCase { private static IPetFactory petFactory=null; public static void testLoader() { try { String baseDir = SystemConfig.PET_LIB_PATH; FileClassLoader classLoader = new FileClassLoader(baseDir); Thread.currentThread().setContextClassLoader(classLoader); String className = SystemConfig.PET_FACTORY_CLASS; petFactory = FactoryManager.getInstance(className); Class petFactoryClass = classLoader.loadClass(className); Logger.info("显示petFactoryClass 的ClassLoader tree:"); ClassLoaderUtil.showLoader4Class(petFactoryClass); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void showPet() { IPet dog=petFactory.buildePet(IPet.Type.DOG); dog.sayHello(); } public static void main(String[] args) { testLoader(); showPet(); } }
将FileClassLoader设置成为线程上下文加载器,然后,在工厂管理器中,使用线程上限加载器加载:
public class FactoryManager { /** * 单例的Pet实例工厂 */ private static IPetFactory petFactory; /** * 已经类名,取得工厂实例 * * @param factoryClassName * @return */ public static IPetFactory getInstance(String factoryClassName) { if (null != petFactory)
{
return petFactory;
} if (null == factoryClassName)
{
//默认的实现
factoryClassName ="com.crazymakercircle.petStore.pet.factory.PetFactoryImpl"; } /** * 获得线程上下文加载器 */ ClassLoader loader = Thread.currentThread().getContextClassLoader(); try {
Class<?> factoryClass = loader.loadClass(factoryClassName);
petFactory = (IPetFactory) factoryClass.newInstance(); } catch (Exception e) { e.printStackTrace(); } return petFactory; } }
源码:
代码工程: classLoaderDemo.zip
下载地址:在疯狂创客圈QQ群文件共享。
疯狂创客圈:如果说Java是一个武林,这里的聚集一群武痴, 交流编程体验心得
QQ群链接:疯狂创客圈QQ群
无编程不创客,无案例不学习。 一定记得去跑一跑案例哦
类加载器系列全目录
5. 入门案例:自定义一个文件系统的自定义classLoader
8. 高级案例1:使用ASM技术,结合类加载器,解密AOP原理
Java类加载器( 死磕9)的更多相关文章
- Java类加载器(死磕 1-2)
Java类加载器( CLassLoader ) 死磕 1.2: 导入 & 类加载器分类 本小节目录 1.导入 1.1. 从class文件的载入开始 1.2. 什么是类加载器 2. JA ...
- Java类加载器(死磕5)
Java类加载器( CLassLoader ) 死磕5: 自定义一个文件系统classLoader 本小节目录 5.1. 自定义类加载器的基本流程 5.2. 入门案例:自定义文件系统类加载器 5 ...
- Java类加载器( 死磕7)
[正文]Java类加载器( CLassLoader )死磕7: 基于加密的自定义网络加载器 本小节目录 7.1. 加密传输Server端的源码 7.2. 加密传输Client端的源码 7.3. 使 ...
- Java类加载器( 死磕8)
[正文]Java类加载器( CLassLoader ) 死磕 8: 使用ASM,和类加载器实现AOP 本小节目录 8.1. ASM字节码操作框架简介 8.2. ASM和访问者模式 8.3. 用于增 ...
- Java类加载器( 死磕 6)
[正文]Java类加载器( CLassLoader )死磕 6: 自定义网络类加载器 本小节目录 6.1. 自定义网络类加载器的类设计 6.2. 文件传输Server端的源码 6.3. 文件传输C ...
- Java类加载器( 死磕 4)
[正文]Java类加载器( CLassLoader ) 死磕 之4: 神秘的双亲委托机制 本小节目录 4.1. 每个类加载器都有一个parent父加载器 4.2. 类加载器之间的层次关系 4.3. ...
- Java类加载器(死磕3)
[正文]Java类加载器( CLassLoader ) 死磕3: 揭秘 ClassLoader抽象基类 本小节目录 3.1. 类的加载分类:隐式加载和显示加载 3.2. 加载一个类的五步工作 3. ...
- java笔记--理解java类加载器以及ClassLoader类
类加载器概述: java类的加载是由虚拟机来完成的,虚拟机把描述类的Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成能被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制 ...
- java类加载器深入研究
看了下面几篇关于类的加载器的文章,豁然开朗.猛击下面的地址开始看吧. Java类加载原理解析 深入探讨 Java 类加载器 分析BootstrapClassLoader/ExtClassLo ...
随机推荐
- word文档的导出(用freemarker模板导出)(桃)
1.将要导出的word文档另存为xml格式的 2.用文档编辑器打开(如:notepad++),将要展示的数据用${name}的形式替换,“name”对应数据库中的字段 3.根据模板生成 package ...
- 苹果safari支持flex布局
今天突然发现flex布局不支持safari 解决办法 .page-wrap { display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */ disp ...
- 转 Python爬虫实战二之爬取百度贴吧帖子
静觅 » Python爬虫实战二之爬取百度贴吧帖子 大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 本篇目标 ...
- 数据结构自己实现——Tree and Forest
//中D序??遍???历???二t叉?树??? //先??序??遍???历???二t叉?树??? //后??序??遍???历???二t叉?树??? #include <iostream> ...
- MySQL通用编程
第一阶段:基础入门 第一章:关系模型 第二章:基本查询 第三章:复杂查询 第四章:权限控制 第五章:查询优化 第二阶段:模型设计 第六章:设计选择 第七章:函数依赖 第八章:分解算法 第九章:设计过程 ...
- 洛谷—— P1342 请柬
https://www.luogu.org/problemnew/show/1342 题目描述 在电视时代,没有多少人观看戏剧表演.Malidinesia古董喜剧演员意识到这一事实,他们想宣传剧院,尤 ...
- How to Mount a USB Drive in Ubuntu
Read more : http://www.ehow.com/how_6762235_mount-usb-drive-ubuntu.html Most USB drives will automou ...
- 深刻理解JavaScript---闭包
JavaScript 闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中).换句话说,这些函数可以“记忆”它被创建时候的环境.——这句话其实有点难以理解.我觉 ...
- 跟开涛学SpringMVC(4.1):Controller接口控制器详解(1)
http://www.importnew.com/19397.html http://blog.csdn.net/u014607184/article/details/52074530 https:/ ...
- spring事务再次理解
2.2.3 只读 事务的第三个特性是它是否为只读事务.如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化.通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认 ...