zoukankan      html  css  js  c++  java
  • 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群


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


    类加载器系列全目录

    1.导入

    2. JAVA类加载器分类

    3. 揭秘ClassLoader抽象基类

    4. 神秘的双亲委托机制

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

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

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

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

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

  • 相关阅读:
    luogu P3174 毛毛虫
    P3386二分图最大匹配模版
    P4180 严格次小生成树
    差分约束
    高斯消元
    P1306 斐波那契公约数
    极值
    排序
    P1852 [国家集训队]跳跳棋
    高精度模版
  • 原文地址:https://www.cnblogs.com/crazymakercircle/p/9824148.html
Copyright © 2011-2022 走看看