June 6, 2003

Q: When should I use Thread.getContextClassLoader() ?

A: Although not frequently asked, this question is rather tough to correctly answer. It usually comes up during framework programming, when a good deal of dynamic class and resource loading goes on. In general, when loading a resource dynamically, you can choose from at least three classloaders: the system (also referred to as the application) classloader, the current classloader, and the current thread context classloader. The question above refers to the latter. Which classloader is the right one?

One choice I dismiss easily: the system classloader. This classloader handles -classpath and is programmatically accessible as ClassLoader.getSystemClassLoader(). All ClassLoader.getSystemXXX()API methods are also routed through this classloader. You should rarely write code that explicitly uses any of the previous methods and instead let other classloaders delegate to the system one. Otherwise, your code will only work in simple command-line applications, when the system classloader is the last classloader created in the JVM. As soon as you move your code into an Enterprise JavaBean, a Web application, or a Java Web Start application, things are guaranteed to break.

So, now we are down to two choices: current and context classloaders. By definition, a current classloader loads and defines the class to which your current method belongs. This classloader is implied when dynamic links between classes resolve at runtime, and when you use the one-argument version of Class.forName()Class.getResource(), and similar methods. It is also used by syntactic constructs like X.class class literals (see "Get a Load of That Name!" for more details).

Thread context classloaders were introduced in Java 2 Platform, Standard Edition (J2SE). Every Thread has a context classloader associated with it (unless it was created by native code). It is set via the Thread.setContextClassLoader() method. If you don't invoke this method following a Thread's construction, the thread will inherit its context classloader from its parent Thread. If you don't do anything at all in the entire application, all Threads will end up with the system classloader as their context classloader. It is important to understand that nowadays this is rarely the case since Web and Java 2 Platform, Enterprise Edition (J2EE) application servers utilize sophisticated classloader hierarchies for features like Java Naming and Directory Interface (JNDI), thread pooling, component hot redeployment, and so on.

Why do thread context classloaders exist in the first place? They were introduced in J2SE without much fanfare. A certain lack of proper guidance and documentation from Sun Microsystems likely explains why many developers find them confusing.

Learn Java from beginning concepts to advanced design patterns in this comprehensive 12-part course! ]

In truth, context classloaders provide a back door around the classloading delegation scheme also introduced in J2SE. Normally, all classloaders in a JVM are organized in a hierarchy such that every classloader (except for the primordial classloader that bootstraps the entire JVM) has a single parent. When asked to load a class, every compliant classloader is expected to delegate loading to its parent first and attempt to define the class only if the parent fails.

Sometimes this orderly arrangement does not work, usually when some JVM core code must dynamically load resources provided by application developers. Take JNDI for instance: its guts are implemented by bootstrap classes in rt.jar(starting with J2SE 1.3), but these core JNDI classes may load JNDI providers implemented by independent vendors and potentially deployed in the application's -classpath. This scenario calls for a parent classloader (the primordial one in this case) to load a class visible to one of its child classloaders (the system one, for example). Normal J2SE delegation does not work, and the workaround is to make the core JNDI classes use thread context loaders, thus effectively "tunneling" through the classloader hierarchy in the direction opposite to the proper delegation.

By the way, the previous paragraph may have reminded you of something else: Java API for XML Parsing (JAXP). Yes, when JAXP was just a J2SE extension, the XML parser factories used the current classloader approach for bootstrapping parser implementations. When JAXP was made part of the J2SE 1.4 core, the classloading changed to use thread context classloaders, in complete analogy with JNDI (and confusing many programmers along the way). See what I mean by lack of guidance from Sun?

After this introduction, I have come to the crux of the matter: neither of the remaining two choices is the right one under all circumstances. Some believe that thread context classloaders should become the new standard strategy. This, however, creates a very messy classloading picture if various JVM threads communicate via shared data, unless all of them use the same context loader instance. Furthermore, delegating to the current classloader is already a legacy rule in some existing situations like class literals or explicit calls to Class.forName() (which is why, by the way, I recommend (again, see "Get a Load of That Name!") avoiding the one-argument version of this method). Even if you make an explicit effort to use only context loaders whenever you can, there will always be some code not under your control that delegates to the current loader. This uncontrolled mixing of delegation strategies sounds rather dangerous.

To make matters worse, certain application servers set context and current classloaders to different ClassLoader instances that have the same classpaths and yet are not related as a delegation parent and child. Take a second to think about why this is particularly horrendous. Remember that the classloader that loads and defines a class is part of the internal JVM's ID for that class. If the current classloader loads a class X that subsequently executes, say, a JNDI lookup for some data of type Y, the context loader could load and define Y. This Y definition will differ from the one by the same name but seen by the current loader. Enter obscure class cast and loader constraint violation exceptions.

This confusion will probably stay with Java for some time. Take any J2SE API with dynamic resource loading of any kind and try to guess which loading strategy it uses. Here is a sampling:

  • JNDI uses context classloaders
  • Class.getResource() and Class.forName() use the current classloader
  • JAXP uses context classloaders (as of J2SE 1.4)
  • java.util.ResourceBundle uses the caller's current classloader
  • URL protocol handlers specified via java.protocol.handler.pkgssystem property are looked up in the bootstrap and system classloaders only
  • Java Serialization API uses the caller's current classloader by default

Those class and resource loading strategies must be the most poorly documented and least specified area of J2SE.

What is a Java programmer to do?

If your implementation is confined to a certain framework with articulated resource loading rules, stick to them. Hopefully, the burden of making them work will be on whoever has to implement the framework (such as an application server vendor, although they don't always get it right either). For example, always use Class.getResource() in a Web application or an Enterprise JavaBean.

In other situations, you might consider using a solution I have found useful in personal work. The following class serves as a global decision point for acquiring the best classloader to use at any given time in the application (all classes shown in this article are available with the download):

public abstract class ClassLoaderResolver
{
/**
* This method selects the best classloader instance to be used for
* class/resource loading by whoever calls this method. The decision
* typically involves choosing between the caller's current, thread context,
* system, and other classloaders in the JVM and is made by the {@link IClassLoadStrategy}
* instance established by the last call to {@link #setStrategy}.
*
* @return classloader to be used by the caller ['null' indicates the
* primordial loader]
*/
public static synchronized ClassLoader getClassLoader ()
{
final Class caller = getCallerClass (0);
final ClassLoadContext ctx = new ClassLoadContext (caller); return s_strategy.getClassLoader (ctx);
}
public static synchronized IClassLoadStrategy getStrategy ()
{
return s_strategy;
}
public static synchronized IClassLoadStrategy setStrategy (final IClassLoadStrategy strategy)
{
final IClassLoadStrategy old = s_strategy;
s_strategy = strategy; return old;
} /**
* A helper class to get the call context. It subclasses SecurityManager
* to make getClassContext() accessible. An instance of CallerResolver
* only needs to be created, not installed as an actual security
* manager.
*/
private static final class CallerResolver extends SecurityManager
{
protected Class [] getClassContext ()
{
return super.getClassContext ();
} } // End of nested class /*
* Indexes into the current method call context with a given
* offset.
*/
private static Class getCallerClass (final int callerOffset)
{
return CALLER_RESOLVER.getClassContext () [CALL_CONTEXT_OFFSET +
callerOffset];
} private static IClassLoadStrategy s_strategy; // initialized in private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if this class is redesigned
private static final CallerResolver CALLER_RESOLVER; // set in static
{
try
{
// This can fail if the current SecurityManager does not allow
// RuntimePermission ("createSecurityManager"): CALLER_RESOLVER = new CallerResolver ();
}
catch (SecurityException se)
{
throw new RuntimeException ("ClassLoaderResolver: could not create CallerResolver: " + se);
} s_strategy = new DefaultClassLoadStrategy ();
}
} // End of class.

You acquire a classloader reference by calling the ClassLoaderResolver.getClassLoader() static method and use the result to load classes and resources via the normal java.lang.ClassLoader API. Alternatively, you can use this ResourceLoader API as a drop-in replacement for java.lang.ClassLoader:

public abstract class ResourceLoader
{
/**
* @see java.lang.ClassLoader#loadClass(java.lang.String)
*/
public static Class loadClass (final String name)
throws ClassNotFoundException
{
final ClassLoader loader = ClassLoaderResolver.getClassLoader (1); return Class.forName (name, false, loader);
}
/**
* @see java.lang.ClassLoader#getResource(java.lang.String)
*/
public static URL getResource (final String name)
{
final ClassLoader loader = ClassLoaderResolver.getClassLoader (1); if (loader != null)
return loader.getResource (name);
else
return ClassLoader.getSystemResource (name);
}
... more methods ...
} // End of class

The decision of what constitutes the best classloader to use is factored out into a pluggable component implementing the IClassLoadStrategy interface:

public interface IClassLoadStrategy
{
ClassLoader getClassLoader (ClassLoadContext ctx);
} // End of interface

To help IClassLoadStrategy make its decision, it is given a ClassLoadContext object:

public class ClassLoadContext
{
public final Class getCallerClass ()
{
return m_caller;
} ClassLoadContext (final Class caller)
{
m_caller = caller;
} private final Class m_caller;
} // End of class

ClassLoadContext.getCallerClass() returns the class whose code calls into ClassLoaderResolver or ResourceLoader. This is so that the strategy implementation can figure out the caller's classloader (the context loader is always available as Thread.currentThread().getContextClassLoader()). Note that the caller is determined statically; thus, my API does not require existing business methods to be augmented with extra Class parameters and is suitable for static methods and initializers as well. You can augment this context object with other attributes that make sense in your deployment situation.

All of this should look like a familiar Strategy design pattern to you. The idea is that decisions like "always context loader" or "always current loader" get separated from the rest of your implementation logic. It is hard to know ahead of time which strategy will be the right one, and with this design, you can always change the decision later.

I have a default strategy implementation that should work correctly in 95 percent of real-life situations:

Find a way out of the ClassLoader maze的更多相关文章

  1. 【译文】走出Java ClassLoader迷宫 Find a way out of the ClassLoader maze

    本文是一篇译文.原文:Find a way out of the ClassLoader maze 对于类加载器,普通Java应用开发人员不需要了解太多.但对于系统开发人员,正确理解Java的类加载器 ...

  2. 使用自定义 classloader 的正确姿势

    详细的原理就不多说了,网上一大把, 但是, 看了很多很多, 即使看了jdk 源码, 说了罗里吧嗦, 还是不很明白: 到底如何正确自定义ClassLoader, 需要注意什么 ExtClassLoade ...

  3. Atitti 载入类的几种方法    Class.forName ClassLoader.loadClass  直接new

    Atitti 载入类的几种方法    Class.forName ClassLoader.loadClass  直接new 1.1. 载入类的几种方法    Class.forName ClassLo ...

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

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

  5. Backtracking algorithm: rat in maze

    Sept. 10, 2015 Study again the back tracking algorithm using recursive solution, rat in maze, a clas ...

  6. Class.forName和ClassLoader.loadClass等

    Class类 首先,Class类里可以记载所有类的属性.方法等信息.这个也就是运行时类别标记,它记录了所有的对象(比如int,MyClass,void,数组等等)对应的类信息. Class对象 JVM ...

  7. Java ClassLoader 原理详细分析(转)

    转载自:http://www.codeceo.com/article/java-classloader.html 一.什么是ClassLoader? 大家都知道,当我们写好一个Java程序之后,不是管 ...

  8. [Tomcat] Tomcat的classloader

    定义 同其他服务器应用一样,tomcat安装了各种classloader(classes that implement java.lang.ClassLoader) Bootstrap | Syste ...

  9. java中Class.forName("xxx")和ClassLoader().loadClass("xxx")的区别

    一.首先,查看Class类中的forName方法,可以发现有如下三个方法,但是我们通常用的是只有一个参数的方法. 简单介绍一下这三个方法: 第一个方法Class.forName("xxx&q ...

随机推荐

  1. 【BZOJ】3527: [Zjoi2014]力

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3527 把${f_i}$消去之后换元就是卷积的形式,直接算就可以了. #include< ...

  2. django 应用中获取访问者ip地址

    通常访问者的IP就在其中,所以我们可以用下列方法获取用户的真实IP: #X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP 代理或者负载 ...

  3. 3. 使用vue-cli创建项目

    eslint: 用来做项目编码规范检查的工具基本原理: 定义了很多规则, 检查项目的代码一旦发现违背了某个规则就输出相应的提示信息有相应的配置, 可定制检查 1. 创建项目 vue脚手架(vue-cl ...

  4. N的阶乘(10000) 51 nod——1057 (大数)

    像这些大整数加法或者乘法什么的思想都一样,就是截位存取,累积进位,最后逆序输出就可以啦 PS:小生是用10000来存取的,300MS就能A,如果单个存取有点危险,题目时间限制好像是1000ms,大家可 ...

  5. [c][c++]按位操作

    因为有时候需要大量的标志位来判断当前状态等.使用太多的int,bool等会使得程序不“漂亮” 这时候需要“位”操作来解决 建立一个标志位 unsigned ; 在定义一些宏,如 #define CON ...

  6. css3动画实现伪弹幕效果

    如图所示: 效果还可以直接用麦唱APP把一首歌分享到微信里面看到,方法类似全民K歌的方法,都是用css3动画实现的, 代码如下:(这是我做真实效果前的一个dome) 直接粘代码就可以看到效果,里面有两 ...

  7. ie67的冷知识

    1. _display:inline;是什么意思 只有ie6认识带下划线的,一般这种写法是用来消除ie6对margin双倍的bug的,比如你写margin-left:10px;那么ie6下显示的是20 ...

  8. C#中统计一个过程消耗的时间

    使用Unity进行的测试,代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; us ...

  9. 动态规划-Race Car

    2018-10-26 21:06:54 问题描述: 问题求解: 方法一.BFS 首先将使用BFS进行解空间的遍历,也就是将本问题转化成了搜索问题,但是有两个地方需要注意: 1.状态保存的问题,每个位置 ...

  10. (转)winform之RichTextBox

    RichTextBox是一种可用于显示.输入和操作格式文本,除了可以实现TextBox的所有功能,还能提供富文本的显示功能. 控件除具有TextBox 控件的所有功能外,还能设定文字颜色.字体和段落格 ...