【正文】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群

无编程不创客,无案例不学习。 一定记得去跑一跑案例哦

类加载器系列全目录

1.导入

2. JAVA类加载器分类

3. 揭秘ClassLoader抽象基类

4. 神秘的双亲委托机制

5. 入门案例:自定义一个文件系统的自定义classLoader

6. 基础案例:自定义一个网络类加载器

7. 中级案例:设计一个加密的自定义网络加载器

8. 高级案例1:使用ASM技术,结合类加载器,解密AOP原理

9. 高级案例2:上下文加载器原理和案例

转载于:https://blog.51cto.com/14033611/2306957

Java类加载器( 死磕9)相关推荐

  1. 深入探讨 Java 类加载器

    深入探讨 Java 类加载器 类加载器(class loader)是 Java™中的一个很重要的概念.类加载器负责加载 Java 类的字节代码到 Java 虚拟机中.本文首先详细介绍了 Java 类加 ...

  2. 80070583类不存在_结合JVM源码谈Java类加载器

    一.前言 之前文章 加多:ClassLoader解惑​zhuanlan.zhihu.com 从Java层面讲解了Java类加载器的原理,这里我们结合JVM源码在稍微深入讲解下. 二.Java类加载器的 ...

  3. java类加载器_类加载器

    回顾一下类加载过程 类加载过程:加载->连接->初始化.连接过程又可分为三步:验证->准备->解析. 一个非数组类的加载阶段(加载阶段获取类的二进制字节流的动作)是可控性最强的 ...

  4. java 类加载器 解密_JAVA类加载器总结整理

    一.What(是什么?) 1.概念 Java类加载器是Java运行时环境的一部分,负责动态加载Java类到JVM的内存空间中.每个Java类必须由某个类加载器装入到内存中.每一个类加载器都有一个父类加 ...

  5. Java类加载器总结

    转载自  Java类加载器总结 1.类的加载过程   JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,如下图所示: 1) 装载:查 ...

  6. java 调用scala 类_如何使用java类加载器调用带参数的scala函数?

    我正在寻找一些将scala jar加载到java类加载器的指导. 当我使用java jar文件时,下面的函数对我有效. 其中,arr是一个java.net.URL数组,用于我需要加载到类加载器中的所有 ...

  7. java 类加载器-基础

    java 类加载器-基础 类加载机制 类加载器的双亲委托机制 自定义类加载路径 自定义类加载器 类加载机制 类加载器ClassLoader. – 负责查找,加载,校验字节码的应用程序. – java. ...

  8. 【java】 Java 类加载器 破坏双亲委派

    1.概述 深入探讨 Java 类加载器 Java双亲委派模型:为什么要双亲委派?如何打破它?破在哪里? Java中的双亲委派机制以及如何打破

  9. java类加载器正确的是_Java面试题:面向对象,类加载器,JDBC, Spring 基础概念

    1. 为什么说Java是一门平台无关语言? 平台无关实际的含义是"一次编写到处运行".Java 能够做到是因为它的字节码(byte code)可以运行在任何操作系统上,与底层系统无 ...

最新文章

  1. 使用SVCUtil.exe生成客户端代理类和配置文件
  2. Open ID Connect(OIDC)在 ASP.NET Core中的应用
  3. 使用 dispatchEvent() 方法
  4. UML分析AsyncDisplayKit框架-ASMuplexImageNode异步下载时序图。
  5. ubuntu java classpath 设置_在Ubuntu中正确设置java classpath和java_home
  6. 什么是扩展现实(XR)?云XR系统怎样实现?终于有人讲明白了
  7. 使用Swagger UI的Document和Test API
  8. adb logcat 保存_adb logcat命令查看并过滤android输出log
  9. cart算法_ID3、C4.5、CART决策树算法
  10. java基础总结06-常用api类-BigDecimal-精确计算
  11. 计算机视觉中的论文常见单词总结
  12. No rule to make target
  13. VMware虚拟机vmx文件丢失
  14. 贪婪洞窟2服务器维护,贪婪洞窟211月30日更新维护公告 贪婪洞窟211月30日更新了那些内容...
  15. 解决androidstudio unable to delete directory的办法
  16. 北京信息科技大学CSDN高校俱乐部举办的首次活动------考研经验交流会
  17. 自我管理的经典书籍推荐:《自我管理必读12篇》
  18. 流感病毒爆发,科技带来希望?谷歌成功预测H1N1流感病毒
  19. 三个短视频剪辑技巧分享,控制时长很关键,轻松剪出高质量视频
  20. VMware Workstation创建Windows 11 Insider Preview (Dev Channel) - Build 25179虚拟机

热门文章

  1. 让数据库支持SQL 2005 CLR 的必要条件
  2. 使用TextRange获取输入框中光标的位置
  3. Shared Event-loop for Same-Origin Windows(译)
  4. img = img1*mask + img2*(1-mask) How do that ?
  5. 【Coursera】Third Week(1)
  6. (原+转)使用opencv的DFT计算卷积
  7. 如何解决 错误code signing is required for product type 'xxxxx' in SDK 'iOS 8.2'
  8. [转]phonegap 2.9 IOS Xcode 搭建环境
  9. Google在KDD2013上关于CTR的一篇论文
  10. 河南省某炮旅的RAID5恢复