Java类加载器( 死磕9)

文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:尼恩Java面试宝典 最新版 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取


【正文】Java类加载器( CLassLoader ) 死磕9:

上下文加载器原理和案例

本小节目录

9.1. 父加载器不能访问子加载器的类
9.2. 一个宠物工厂接口
9.3. 一个宠物工厂管理类
9.4 APPClassLoader不能访问子加载器中的类
9.5. 线程上下文类加载器

说明:本文会以pdf格式持续更新,更多最新尼恩3高pdf笔记,请从下面的链接获取:语雀 或者 码云

类加载器的代理模式并不能解决 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<span style="color: rgb(0, 0, 255);">&lt;?</span>&gt;   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<span style="color: rgb(0, 0, 255);">&lt;?</span>&gt; factoryClass = loader.loadClass(factoryClassName);
        petFactory = (IPetFactory) factoryClass.newInstance();

    } catch (Exception e)
    {
        e.printStackTrace();
    }
    return petFactory;
  }
}

源码:

代码工程: classLoaderDemo.zip

下载地址:下面链接获取。

疯狂创客圈:如果说Java是一个武林,这里的聚集一群武痴, 交流编程体验心得

说明:本文会以pdf格式持续更新,更多最新尼恩3高pdf笔记,请从下面的链接获取:语雀 或者 码云

posted @ 2018-10-21 09:44  疯狂创客圈  阅读(884)  评论(0编辑  收藏  举报