【正文】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:上下文加载器原理和案例

Java类加载器( 死磕9)的更多相关文章

  1. Java类加载器(死磕 1-2)

      Java类加载器(  CLassLoader ) 死磕 1.2:  导入 & 类加载器分类 本小节目录 1.导入 1.1. 从class文件的载入开始 1.2. 什么是类加载器 2. JA ...

  2. Java类加载器(死磕5)

    Java类加载器(  CLassLoader )  死磕5:  自定义一个文件系统classLoader 本小节目录 5.1. 自定义类加载器的基本流程 5.2. 入门案例:自定义文件系统类加载器 5 ...

  3. Java类加载器( 死磕7)

    [正文]Java类加载器(  CLassLoader )死磕7:  基于加密的自定义网络加载器 本小节目录 7.1. 加密传输Server端的源码 7.2. 加密传输Client端的源码 7.3. 使 ...

  4. Java类加载器( 死磕8)

    [正文]Java类加载器(  CLassLoader ) 死磕 8:  使用ASM,和类加载器实现AOP 本小节目录 8.1. ASM字节码操作框架简介 8.2. ASM和访问者模式 8.3. 用于增 ...

  5. Java类加载器( 死磕 6)

    [正文]Java类加载器(  CLassLoader )死磕 6:  自定义网络类加载器 本小节目录 6.1. 自定义网络类加载器的类设计 6.2. 文件传输Server端的源码 6.3. 文件传输C ...

  6. Java类加载器( 死磕 4)

    [正文]Java类加载器(  CLassLoader ) 死磕 之4:  神秘的双亲委托机制 本小节目录 4.1. 每个类加载器都有一个parent父加载器 4.2. 类加载器之间的层次关系 4.3. ...

  7. Java类加载器(死磕3)

    [正文]Java类加载器(  CLassLoader ) 死磕3:  揭秘 ClassLoader抽象基类 本小节目录 3.1. 类的加载分类:隐式加载和显示加载 3.2. 加载一个类的五步工作 3. ...

  8. java笔记--理解java类加载器以及ClassLoader类

    类加载器概述: java类的加载是由虚拟机来完成的,虚拟机把描述类的Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成能被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制 ...

  9. java类加载器深入研究

    看了下面几篇关于类的加载器的文章,豁然开朗.猛击下面的地址开始看吧. Java类加载原理解析      深入探讨 Java 类加载器 分析BootstrapClassLoader/ExtClassLo ...

随机推荐

  1. 转 C++构造函数、析构函数、虚函数之间的关系

    C++构造函数.析构函数.虚函数之间的关系 1. 如果我们定义了一个构造函数,编译器就不会再为我们生成默认构造函数了.2. 编译器生成的析构函数是非虚的,除非是一个子类,其父类有个虚析构,此时的函数虚 ...

  2. 转 Perl函数返回值用法指导

    http://developer.51cto.com/art/201007/213003.htm Perl函数返回值用法指导   Perl编程语言中Perl函数返回值用法你是否比较熟悉,这里向大家简单 ...

  3. 优先队列priority_queue的使用

    优先队列是队列的一种,不过它可以按照自定义的一种方式(数据的优先级)来对队列中的数据进行动态的排序. 每次的push和pop操作,队列都会动态的调整,以达到我们预期的方式来存储.例如:我们常用的操作就 ...

  4. 在 Mac OS X 环境中从源代码编译安装 FFmpeg

    最近因为一个项目要求,需要开发实时视频编解码功能,准备采用 FFmpeg 以 x264 方式进行实时的视频编解码.Windows 以及 Linux 环境下的 FFmpeg 动态库.头文件等资源都非常容 ...

  5. js-浅显基础-正则表达式集

    今天进博客园,忽然发现我也是有粉丝的人啦!!!!!!!!谢谢谢谢关注(爱心爱心) 本来不想做这个笔记的,但是每次都百度我自己都烦死了,所以还是自己整理一下方便我后期使用: 独笑笑不如众笑笑,放轻松点 ...

  6. 多核服务器的JVM优化选项(转载)

    原文链接 现在多核CPU是主流.利用多核技术,可以有效发挥硬件的能力,提升吞吐量,对于Java程序,可以实现并发垃圾收集.但是Java利用多核技术也带来了一些问题,主要是多线程共享内存引起了.目前内存 ...

  7. ORACLE MOS 翻译

    http://blog.csdn.net/msdnchina/article/details/53174196

  8. 计算广告、推荐系统论文以及DSP综述

    http://www.huxmarket.com/detail/2966 DSP场景假定前提: 以CTR预估为例,向广告主以CPC(OCPC)方式收费,向ADX以CPM方式付费.投放计划受预算限制,在 ...

  9. 需要配置执行path?no

    下面的代码以管理员权限运行即可,保存为bat 2018/2/6(basic) - 初始化版本 @echo off :continue echo 输入exit退出 set /p My_PATH=请输入要 ...

  10. Amazon SNS移动推送更新——新增百度云推送和Windows平台支持

    Amazon SNS(Simple Notification Service)是一种基于云平台的消息通知和推送服务. SNS提供简单的 Web 服务接口和基于浏览器的管理控制台让用户可以简易设置.执行 ...