Java ClassLoader and Dependency Resolution

2020-07-13 07:30发布

问题:

Can someone clarify that the role of a ClassLoader is not only to load an individual class, but also its dependencies? And if so, what exactly does the entire process entail? I'm looking for implementation detail if at all possible.

For example, at some point, bytes are going to have to be read in from somewhere (a network or filesystem location), and file system locations are going to have to be calculated on the basis of a classes canonical name and a foreknowledge of class paths available to the JVM- how does an individual ClassLoader try to locate a file over potentially multiple class-paths? Where does it get this information from? Also, at what point are a class files bytes verified and its dependencies examined for availability?

As much detail as possible would be appreciated :)

回答1:

ClassLoading is a very complex subject. The ClassLoader and Java security model are inextricably tied together. Essentially the JVM loads classes on demand. When there is a hierarchy of classloaders, the JVM attempts to resolve the class as far down the chain as possible. In short, if the class is defined in the "boot" classloader and in an application defined class loader, it will always use the version in the boot classloader.

Within a classloader, such as the URLClassLoader, the search order is the order in which you've told it to look. Essentially the array of URLs you told it had classes will be searched from the first entry to the last.

When the class that you defined references another class, that class is also resolved using the same algorithm. But here's the catch: it only resolves it relative to where it was found. Let's take the scenario where the class SomeCoolThing is in the boot classloader, but depends on SomeLameThing, which is in an application defined classloader. The process would look like this:

App-ClassLoader: resolveClass("SomeCoolThing")
    parent->resolveClass("SomeCoolThing")

Boot-ClassLoader (the ultimate parent): resolveClass("SomeCoolThing")
    SomeCoolThing needs SomeLameThing
    resolveClass("SomeLameThing") // Can't find SomeLameThing!!!!

Even though SomeLameThing is in the classloader where you requested SomeCoolThing, SomeCoolThing was resolved in a different classloader. That other classloader has no knowledge of the child classloader, and tries to resolve it itself and fails.

I had a book a long time ago that covered the Java ClassLoaders in really good depth, and I recommend it. It's Java Security by O'Reilly Media. It will answer every question you never wanted to know, but still need to, when dealing with ClassLoaders and how they work.



回答2:

I can answer some of your questions:

how does an individual ClassLoader try to locate a file over potentially multiple class-paths?

If you mean different class loaders have different classpaths then each class loader takes the properties (i.e. classpath) of the parent class loader. All things equal each class loader has the same classpath as any other (I believe; not sure if the JVM does anything weird internally). So MyClass.class is the same for a class loader and all child class loaders. If you have multiple MyClass.class defined on the same class path then the JVM picks up the first one. In the past I've created my own class loader and prepended a custom classpath onto the existing classpath to load classes at runtime that were not on the classpath when launched.

The get to the nuts and bolts of it I'm sure there is a spec out there that describes this or you could download the JVM code (the assembly/C/C++ code) and go though that but I've had to do that and "it ain't pretty".

Of course "they" are changing the classpath stuff in 1.7 so I'm not sure how that is going to work...

Hope that helps a bit...