惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Check Point Blog
U
Unit 42
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Martin Fowler
Martin Fowler
L
LangChain Blog
博客园_首页
博客园 - 【当耐特】
Vercel News
Vercel News
I
InfoQ
GbyAI
GbyAI
爱范儿
爱范儿
D
DataBreaches.Net
Blog — PlanetScale
Blog — PlanetScale
B
Blog RSS Feed
A
About on SuperTechFans
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
G
Google Developers Blog
大猫的无限游戏
大猫的无限游戏
Apple Machine Learning Research
Apple Machine Learning Research
F
Fortinet All Blogs
N
Netflix TechBlog - Medium
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Proofpoint News Feed
美团技术团队
V
V2EX
Stack Overflow Blog
Stack Overflow Blog
有赞技术团队
有赞技术团队
Y
Y Combinator Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
H
Help Net Security
Recent Announcements
Recent Announcements
Microsoft Azure Blog
Microsoft Azure Blog
D
Docker
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
量子位
小众软件
小众软件
J
Java Code Geeks
S
SegmentFault 最新的问题
Engineering at Meta
Engineering at Meta
Google DeepMind News
Google DeepMind News
MongoDB | Blog
MongoDB | Blog
The Cloudflare Blog
Recorded Future
Recorded Future
阮一峰的网络日志
阮一峰的网络日志
T
The Blog of Author Tim Ferriss
MyScale Blog
MyScale Blog
Microsoft Security Blog
Microsoft Security Blog

博客园 - sunliho

find flow scope varibles in spring web flow Building Liferay on Tomcat Debugging Liferay in Eclipse ssl 服务器证书,客户端信任证书 相对路径与绝对路径 - sunliho - 博客园 db2 command 标准sql Linux系统下架设APACHE SVN服务器全过程 修改ip地址 mysql in linux network configuration in linux Linux系统信息查看命令大全 linux常用命令 Permission denied in linux install jdk in linux generic dao hibernate.cfg.xml 配置(摘录) - sunliho - 博客园 hibernate 配置文件(摘录) - sunliho - 博客园 web.xml配置详解(摘录)
Java ClassLoader and Context ClassLoader
sunliho · 2012-02-15 · via 博客园 - sunliho

Java ClassLoader and Context ClassLoader

Class loading can be a difficult subject in Java.  Mostly it happens automatically.  But when it doesn't and the programmer has to select the correct class loader it's easy to be confused.

There are basically four choices.

  1. Use the system class loader.
  2. Use the current class loader.
  3. Use the context class loader.
  4. Roll your own class loader.


System Class Loader

The first one is easy.  The system class loader loads system stuff.  If it's not part of the Java API, the system class loader won't load it.  This is also the class loader that you configure when set the class path of your application.  So the system class loader will find resources that you've set with the class path.

Remember that all class loaders by default will eventually invoke their parent class loaders.  That means all requests eventually bubble up to the system class loader.  Thus you almost always don't want to use the system class loader directly, since it'll be invoked by any other class loader you call.  Call the current class loader or the context class loader, and the system class loader will be tried at some point if needed.

Note that with choice number four, roll your own, it is possible to make a class loader that does not call its parent.  Some frameworks do this.  Read the damn manual (*cough JSP/Servlets cough*).  You do have to understand what is loaded and when to use those special packages anyway, so read up.  I won't cover any more about those here.

Current Class Loader

The second choice the current class loader.  This is the correct choice much of the time.  To get the current class loader, call getClass().getClassLoader() on your current class.

public MyClass {
  public myMethod() {
    ...
   getClass().getClassLoader();
   ...
  }
}

If in a static context, just use the class literal (You_Class_Name.class) instead of getClass().

public MyClass {
  public static myMethod() {
    ...
   MyClass.class.getClassLoader();
   ...
  }
}

This will load resources from within your own Jar file.  Wherever your Jar goes, this will follow and always load from resources within that Jar.

What is a resource within a Jar file?  I'm glad you asked.  A resource is just a file that you've bundled with the Jar file.  In other words, if you take your application and add a file to it like this:

myJar.jar

Here, I've made a simple Jar file, with a subdirectory next to the Main.class.  Recall that "Main.class" is just what the Java compiler calls your Main.java file after it is compiled.  The subdirectory "images" is just made with a regular "mkdir" command (or whatever your OS uses) and then a file (in this case "Favorites.png") is copied in to the subdirectory just like a regular file.  The entire group of files can be assembled into a single Jar file with a command:

jar -cvf myJar.jar <input_dir>

The jar command will copy all files in a subdirectory by default.  You don't have to list each individual file name.  Don't try to use * or */* or **/*, it'll just mess up.  Here's the command I used.  Note I called "myPackage" "frameimage":

jar -cvfe myJar.jar frameimage.Main -C build/classes frameimage

This command makes "myJar.jar".  The extension is required, I could have named it "myJar.zip" or "fred" if I wanted.  The entry point (e) is set to "frameimage.Main" which is the fully qualified name of my main class.  My IDE normally builds classes in a subdirectory named build/classes so I use the -C switch to pop over there first.  Finally, my entire program is packaged in "frameimage" (package names and subdirectory names are the same in Java) so I just name "frameimage" as my single argument for the jar command, and it fetches all files under that subdirectory and puts them in the Jar file.  Here's the output (because I used the v option).

added manifest
adding: frameimage/(in = 0) (out= 0)(stored 0%)
adding: frameimage/images/(in = 0) (out= 0)(stored 0%)
adding: frameimage/images/Favorites.png(in = 25695) (out= 25629)(deflated 0%)
adding: frameimage/Main$1.class(in = 502) (out= 332)(deflated 33%)
adding: frameimage/Main.class(in = 2077) (out= 1118)(deflated 46%)

The additional class "Main$1.class" is just an anonymous inner class that I use to kick off GUI creation.

Hopefully this provides a 100% bomb-proof expaination of where resources go.  A lot of folks seem confused, and blame their class loader, when what really happened is they put the resource in the wrong spot. 

How does this resource get loaded?

Main.class.getResource( "images/Favorites.png" );

In this case I used a static context, so I used the class literal "Main.class" instead of getClass().  That's it.  The name of the resource is just a relative path to the resource itself.  The subdirectory "images" is in the same package (subdirectory) "frameimage" as Main, so we just load up resources from there as if we were accessing a relative file name.

There's a sample Jar file at the end of this article.  Download it and check it out.

Context Class Loader

According to an article on context class loaders at Java World, context class loaders were intended as a back door for the current class loader discussed above. What happens when a resource isn't part of your Jar file, like above, and might in fact come from any place, including places you have no knowledge of or can't control? 

That's a context class loader.  Context class loaders are attached to threads, not classes.  They're set by the caller, which means that the context class loader can be set to any class loader at all, even one completely different from either the current class loader or the system class loader.  By default, it is "typically set to the class loader used to load the application" (see the Java Thread documentation). 

To me, that implies the system class loader.  For an applet, that may mean the current class loader used by your main class.  Some experimentation may be required if you are not sure.

The context class loader was intended to be used by frameworks.  That is, large libraries which do specialized things in a Java program.  Call setContextClassLoader() to set the class loader for a thread, make a library call, and the library will use the correct class loader (instead of its own, which is probably wrong).

Note that any framework or library must call

Thread.currentThread().getContextClassLoader();

To get the context class loader, it won't happen automatically.  Thus you need to read the documentation on your library to discover it it uses this feature.

Likewise, if a library function uses call backs, it may expect you to uses the context class loader instead of the current class loader.  Again, read the documentation carefully to find out.

Create You Own Class Loader

You can easily create your own class loaders by sub-classing:

public class MyClassLoader extends ClassLoader {
  ....
}

However this is a pretty advanced topic that I can't cover here.  Just know that it exists, and if you do need to know more, Google is your friend.