Java classloaders: why search the parent classloader first?
The correct behaviour for a classloader in Java is to:
- If it has already been loaded, return the class
- Call the parent loadClass()
- Try and load the class itself.
So the class defined in the system classpath should always get loaded first. Tomcat defines classloader per war, which has the system classloader as a parent, so if you try to load a class, it will first look in the system classpath and then in the classpath defined in the war file.
As per my understanding, this is for two reasons:
- To avoid problems with different versions of classes being used. Imagine I redefined java.lang.Object in a war, it would be a nightmare.
- To avoid dependencies on child classloaders: The system cla开发者_如何学Pythonssloader cannot depend on child classloaders: it would be hard to redeploy a war, for instance.
So, the question is:
In addition to the above problems, are there any other pitfalls to implementing a classloader which does not do a parent search first?
Tomcat doesn't looks for a parent classloader first. Actually it does the opposite: it first looks in the webapp and only then goes to the parent classloader (which is "lib" for Tomcat 6/7 and "shared" for Tomcat 5.5). The exception for this rule are the system classes (I think everything that has package java.* and javax.*), these classes were looked only in the system classloader. I believe the reason they do it is the #1 reason you stated.
So basically it's ok to implement parent-first strategy. It's also ok to implement parent-last. Both strategies have their cons and pros.
I'll give you one more reason, why to implement parent-first: you reduce the amount of classes loaded in perm memory. Imagine you have multiple web applications using the same library. With parent-first the library with be loaded once. With parent-last it will be loaded multiple times.
However, with parent-first all web-apps will be required to use the same version of the library, while with parent-last they may use different versions.
Not really. In fact it's as simple as instantiating an URLClassLoader
and giving it null
for the parent:
myClassLoader = new URLClassLoader(myUrlArray, null);
http://download.oracle.com/javase/6/docs/api/java/net/URLClassLoader.html
The only other reason I can think of would be to ensure that the classpath works as expected. By searching the parent first you are in effect putting the parent's classpath (i.e. the system classpath ) before the child's classpath. So if you specify specific jars when you start the jvm using -cp they would "shadow" any jars you've included in any archive that your trying to load. If you don't check the parent first then the child's class path would shadow the parent.
Tarlog is correct, you don't have to do it that way. Java didn't envision use case like tomcat.
However it is questionable why containers host multiple apps in one VM. Separate processes is nicer.
If you do not search your parent at all, you will lose access to all of the standard Java objects, and likely will not be able to run at all.
But it is reasonable to search your own classloader first, before trying the parent. If you want to override behavior that a class above you provides, you would need to do it this way.
The JVM doesn't, because searching your parent first makes the most sense. If you know what you're doing, though, you can do some pretty interesting stuff with class loaders. Take a look at Classworlds.
Tomcat web application class loader doesn't obey "use parent first". As mentioned in docs:
As mentioned above, the web application class loader diverges from the default Java delegation model (in accordance with the recommendations in the Servlet Specification, version 2.4, section 9.7.2 Web Application Classloader). When a request to load a class from the web application's WebappX class loader is processed, this class loader will look in the local repositories first, instead of delegating before looking. There are exceptions. Classes which are part of the JRE base classes cannot be overridden.
精彩评论