Java类加载器( 死磕9)

发布时间:2020-08-03 00:28:06 作者:疯狂创客圈
来源:网络 阅读:207

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

推荐阅读:
  1. 死磕 java同步系列之AQS终篇(面试)
  2. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

编程语言 java ava

上一篇:Django 之 JsonResponse使用方法

下一篇:首屏动画及验证网络状态跳转

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》