Get out of the class loader maze

Source: Internet
Author: User

This is a few days ago when looking at the class loader mechanism search for an old article, the Internet search for the corresponding Chinese materials, feeling a lot of meaning did not translate out, these two days I tried to translate a bit for their fellow reference. English article address: Find A-on-the-ClassLoader maze

Out of the class loader maze (I translate, reproduced please indicate the source)

System class Loader Current class loader Context class Loader which one should you use? ?

by Vladimir Roubtsov, javaworld.com, 06/06/03

June 6, 2003

Q: When do I the with Thread.getcontextclassloader () ?

A: Although this problem is not common, it is difficult to answer correctly. It usually appears in framework programming as a good way to solve the dynamic loading of classes and resources. In general, when loading a resource dynamically, there are at least three kinds of loaders to choose from: The system ClassLoader (also known as the Application ClassLoader), the current class loader , classloader (Current ClassLoader), and the context class loader for the present thread (the classloader ). The problem mentioned above refers to the last loader. Which class loader is correct?

An easy to exclude option: the system class loader . This ClassLoader handles classes and resources under the path specified by the CLASSPATH environment variable, and can be accessed programmatically through the Classloader.getsystemclassloader () method. All Classloader.getsystemxxx () API methods are also accessed through this classloader. You should rarely write code to explicitly invoke it, but instead delegate it to the System class loader with other ClassLoader. Otherwise, your code will only work in a simple command-line application when the system ClassLoader is the last class loader created by the JVM. As long as you migrate the code to the Ejb,web app, or the Java Web Start app, there's definitely a problem.

So now we are two choices: the current ClassLoader and the context class loader . By definition, the current class loader loads and defines the class to which the current method belongs. This classloader is implicitly called when you use the Class.forName () method with a single parameter, the Class.getResource () method, and a similar method when the runtime class is linked. It also appears in a letter call like the X.class syntax. (see "Get a Load of that name!" For more information)

The thread context ClassLoader was introduced in J2SE. Each thread assigns a context classloader (unless the thread is created by local code). The loader is set by the Thread.setcontextclassloader () method. If you do not call this method after the thread is constructed, the threads will inherit the context ClassLoader from its parent thread (the translator note: The parent thread here is the thread that executes the creation of the new Thread object statement). If you do not make any settings throughout the application, all threads will use the System class loader as their own context loader. It is important to understand that since the web and the Java EE Application Server have adopted a complex class loader hierarchy for features such as Jndi, thread pool, component thermal deployment, and so on, this is a rare scenario where the translator notes that the entire application is not set up.

Why is the thread context ClassLoader in the first place? They were introduced into the J2SE without fanfare. The lack of proper guidance and documentation from Sun may explain why many developers are confused about this concept.

In fact, the context ClassLoader provides a backdoor to bypass the load-delegation mechanism of the classes introduced in J2SE. Typically, all class loaders in a JVM are organized into a hierarchy so that each classloader (except the original ClassLoader that starts the entire JVM) has a parent loader. When a class is required to load, each classloader will first be delegated to the parent loader to load, and the current class loader will not load until the parent loader is loaded successfully.

Sometimes this load order does not work, and typically occurs when some JVM core code must dynamically load resources provided by the application developer. As a jndi example: its core content (starting with j2se1.3) is implemented in the boot class in Rt.jar, but these jndi core classes may load Jndi providers implemented and deployed by independent vendors in the application's classpath. This scenario requires a parent class loader (the original ClassLoader in this example, the loader that loads Rt.jar) to load a class that is visible in its subclass loader (System ClassLoader). At this point, the usual J2SE delegation mechanism does not work, and the workaround is to have the Jndi core class use the thread context loader to effectively establish a "channel" in the opposite direction to the ClassLoader hierarchy to achieve the correct delegate.

In addition, the previous paragraph may remind you of something else: Java API (JAXP), which is used as XML parsing. Yes, when JAXP is just an extension of J2SE, the XML parsing factory uses the current class loader as the implementation of the start parser. When JAXP as part of the j2se1.4 core, the class load changes to use the thread context ClassLoader, and the jndi situation is quite similar (confusing many programmers). Do you understand what I mean by the lack of guidance from sun?

After these introductions, take a look at the crux of the problem: the remaining two choices are not in any case correct. Some people think that the threading class loader should program a new standard scheme. However, if multiple JVM threads share data traffic, this will cause a very confusing class loading scenario unless they all use the same context loader instance. Also, the delegate to the current ClassLoader is already an old rule that exists in the case of a class literal call (that is, x.class) or an explicit call to Class.forName () (This is why, incidentally, I recommend avoiding a version with one parameter for this method). Even if you make the effort to use the context loader to the fullest extent possible, there will always be some code that is not under your control but is delegated to the current loader. This uncontrolled mixed-delegation strategy sounds quite dangerous.

To make things worse, some application server settings contexts and the current ClassLoader are different loader instances, making the same classpath but without a parent-child relationship in the delegation mechanism. Take a second to think about why this is especially scary. Remember that the class loader loads and defines a class that has an ID inside the JVM. If the current class loader loads a class X and then executes a jndi lookup for some information about the Y class, the context ClassLoader might load the Y class. This instance of the Y class will be different from the class instance that has the same name and is visible in the current class loader. A load violation constraint exception occurs when a type conversion is forced.

This confusion is likely to persist for some time in Java. Take any of the J2SE APIs loaded with any form of dynamic resources and try to guess which loading strategy to use. Here is a sample:

    • Jndi uses context class loader
    • Class.getResource () and Class.forName () use the current class loader
    • JAXP using the context ClassLoader (as of J2SE 1.4)
    • Java.util.ResourceBundle using the current class loader of the call
    • The URL protocol processor specified by the JAVA.PROTOCOL.HANDLER.PKGS system property is queried only in the boot class loader and the System class loader
    • The Java serialization API defaults to the caller's current class loader

These classes and resource-loading policies are certainly the most undesirable records in J2SE.

a Java What does a programmer have to do?

If your implementation is limited to a certain framework that has explicit resource-loading rules, stick to them. We hope that the burden of their work is on those who implement the framework (such as application server vendors, although they are not always correct). For example, in a Web application or EJB, just use Class.getResource ().

In other cases, you might consider using a solution that I found useful in personal work. The following class is used as a global decision point to get the best class loader for any given time in the application (all sample code can be downloaded from download):

 Public Abstract classClassloaderresolver {/*** This method is provided to the person calling this method to select an instance of the best class loader for class/resource loading. * Typically involves the selection between the caller's current class loader in the JVM, the thread context ClassLoader, the system ClassLoader, and other class loaders.         The loader instance is provided by the * instance of Iclassloadstrategy set by the Setstrategy method. * * @ Returns the ClassLoader instance to the caller [returns Null to represent the JVM's startup ClassLoader]*/          Public Static synchronizedClassLoader getClassLoader () {FinalClass caller = getcallerclass (0); FinalClassloadcontext CTX =NewClassloadcontext (caller); returnS_strategy.getclassloader (CTX); }          Public Static synchronizediclassloadstrategy getstrategy () {returnS_strategy; }          Public Static synchronizedIclassloadstrategy Setstrategy (Finaliclassloadstrategy Strategy) {             FinalIclassloadstrategy old =S_strategy; S_strategy=strategy; returnOld ; }                      /*** A helper class that gets the caller context. The Getclasscontext () method is visible to the * SecurityManager sub-class. Just create an instance of the Callerresolver class * without having to install an actual security manager*/         Private Static Final classCallerresolverextendsSecurityManager {protectedClass [] Getclasscontext () {return Super. Getclasscontext (); }                      } //Nested class End                          /** Gets the current method caller context for the specified offset position*/         Private StaticClass Getcallerclass (Final intCalleroffset) {                      returnCaller_resolver.getclasscontext () [Call_context_offset +Calleroffset]; }                  Private StaticIclassloadstrategy S_strategy;//class is loaded when initialized (see block of static statements below)                 Private Static Final intCall_context_offset = 3;//This value may need to be changed if the class is redesigned        Private Static FinalCallerresolver Caller_resolver;//class is loaded when initialized (see block of static statements below)                 Static         {             Try             {                 //if the current security manager does not have ("Createsecuritymanager") run-time permissions, it may fail:Caller_resolver=NewCallerresolver (); }             Catch(SecurityException se) {Throw NewRuntimeException ("Classloaderresolver:could not create Callerresolver:" +SE); } s_strategy=NewDefaultclassloadstrategy (); } }   //end of class definition

Using the Classloaderresolver.getclassloader () static method to obtain a reference to a class loader, you can use this result to load classes and resources through the generic ClassLoader API. Alternatively, you can use Resourceloader as a simple replacement for the ClassLoader:

 Public Abstract classResourceloader {/**      * @seeJava.lang.classloader#loadclass (java.lang.String)*/       Public StaticClass LoadClass (FinalString name)throwsClassNotFoundException {FinalClassLoader loader = Classloaderresolver.getclassloader (1); returnClass.forName (Name,false, loader); }          /**          * @seeJava.lang.classloader#getresource (java.lang.String)*/               Public StaticURL GetResource (FinalString name) {              FinalClassLoader loader = Classloaderresolver.getclassloader (1); if(Loader! =NULL)                  returnLoader.getresource (name); Else                  returnClassloader.getsystemresource (name); } ... more methods ...} //end of class definition

The policy that determines which classloader to use is implemented by the Iclassloadstrategy interface, which is a pluggable component:

 Public Interface Iclassloadstrategy  {      ClassLoader getclassloader (Classloadcontext ctx);   // end of Interface definition

To help Iclassloadstrategy make a decision, you need to pass in a Classloadcontext object:

 public  class   Classloadcontext { public  final   Class Getcallerclass () { r      Eturn   M_caller; } classloadcontext ( final   Class Calle          R) {M_caller  = caller;  private  final   Class M_caller;  //  class definition ends  

The Classloadcontext.getcallerclass () return class is used for Classloaderresolver or Resourceloader. So that the implementation policy can return the caller's ClassLoader (the context loader can always get through Thread.CurrentThread (). Getcontextclassloader ()). It is important to note that the caller is immutable, so my API does not require existing business methods to add additional class parameters, as well as static methods and initialization methods. You can expand this context object by adding additional properties based on your deployment.

All of these look like a strategy pattern in design mode. The core idea is to separate the "use context ClassLoader" and "Use current ClassLoader" decisions from your other specific implementation logic. It's hard to predict in advance which strategy is right and this design, you can change the strategy at any time.

I have a default policy implementation that works correctly in the case of real work 95%:

 Public classDefaultclassloadstrategyImplementsIclassloadstrategy { PublicClassLoader getClassLoader (FinalClassloadcontext CTX) {          FinalClassLoader Callerloader =Ctx.getcallerclass (). getClassLoader (); FinalClassLoader Contextloader =Thread.CurrentThread (). Getcontextclassloader ();                        ClassLoader result; //if the caller loader and context loader are parent-child relationships, the child loader is always selected:                          if(IsChild (Contextloader, callerloader)) result=Callerloader; Else if(IsChild (Callerloader, contextloader)) result=Contextloader; Else              {                  //else branches can be merged into the previous one, listed separately to emphasize in ambiguous cases:result =Contextloader; }                            FinalClassLoader Systemloader =Classloader.getsystemclassloader (); //Considerations when deploying as a startup class or starting an extension class:            if(IsChild (result, systemloader)) result=Systemloader; returnresult; } ... more methods ...} //end of class definition

The logic above is simple to understand. If the caller's current loader and context loader are parent-child relationships, the subclass loader is always selected. The resources visible by the subclass loader are often also visible to the parent ClassLoader, as long as the J2SE proxy rules are followed, most of which are the correct policies.

It is not possible to give the correct policy when the current loader and context loader are not parent-child relationships. Ideally, the Java runtime should not allow this ambiguous condition. Once this happens, my code chooses the context loader: This strategy is based on my experience of working correctly for most of my time. You can modify the code as needed. The context loader may be a better choice in the framework component, and the current loader may be a better choice in the business logic.

Finally, a simple check ensures that the selected ClassLoader is not the parent loader of the System class loader. This is a good practice if the code you are writing might be deployed as a standard extension library.

Please note that I intentionally did not check the name of the resource or class being loaded. If not unexpectedly, the experience of Java XML APIs that will become part of the J2SE core tells you that filtering based on the class name is not a good idea. I didn't test the load of the class to see which loader had successfully loaded first. Fundamentally, checking the parent-child relationship of the ClassLoader is a better and more predictable approach.

While Java resource loading is still an esoteric topic, with the release of the upgrade J2SE more and more relies on various loading strategies. If this block does not give some significant improvements in the design of Java will be a lot of trouble. Whether you agree or disagree, thank you very much for your feedback and for your personal design experience.

About the author

Vladimir Roubtsov has more than 13 years of programming experience in various languages and started using Java in 1995. Now, he develops enterprise application software as a senior engineer of trilogy company.

Get out of the class loader maze

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.