Parent delegation model
The concept of class loading should be considered an innovation in the Java language. The purpose is to decouple the class loading process from the virtual machine and achieve the purpose of "geting a binary byte stream describing this class through the fully qualified name of the class." The code module that implements this function is the class loader. The basic model of the class loader is the famous Parents Delegation Model. It sounds awesome, but the logic is actually very simple. When we need to load a class, we first determine whether the class has been loaded. If not, we determine whether it has been loaded by the parent loader. If we have not called our own findClass method to try loading. This is the basic model (delete theft image infringement):
It is also very simple to implement. The key is the loadClass method of the ClassLoader class. The source code is as follows:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{ synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { Class(c); } return c; }}Suddenly I felt teased, why did I just throw the exception directly? In fact, it is because the ClassLoader class is an abstract class. In fact, it will write a subclass when used. This method will be rewritten as needed to complete the loading process required by the business.
Custom ClassLoader
When customizing the subclass of ClassLoader, there are two common methods: one is to rewrite the loadClass method, and the other is to rewrite the findClass method. In fact, these two methods are essentially the same. After all, loadClass will also call findClass, but logically speaking, it is best not to directly modify the internal logic of loadClass.
I personally think the better way is to just rewrite the loading method of custom classes in findClass.
Why is this better? Because I also said earlier that the loadClass method is the place to implement the logic of the parent-delegated model. Modifying this method without authorization will lead to the model being destroyed and easily cause problems. Therefore, it is best to make small-scale changes within the framework of the parent delegation model to not destroy the original stable structure. At the same time, it also avoids the need to write duplicate code delegated by parents in the process of rewriting the loadClass method. From the perspective of code reusability, not modifying this method directly is always a better choice.
Of course, it would be different if you deliberately destroy the parent commission model.
Destroy the parent delegation model
Why destroy the parent delegation model?
In fact, in some cases, we may need to load two different classes, but unfortunately, the names of these two classes are exactly the same. At this time, the parent delegation model cannot meet our requirements. We need to rewrite the loadClass method to destroy the parent delegation model and let the same class name load multiple times. Of course, the destruction mentioned here is only destruction in a local sense.
But the class names are the same, how can jvm distinguish these two classes? Obviously, this will not cause any collapse of the worldview. In fact, classes are not only limited by class names in jvm, but also belong to the ClassLoader that loads them. Classes loaded by different ClassLoaders actually do not affect each other.
Do an experiment.
Let's write two classes first:
package com.mythsman.test;public class Hello { public void say() { System.out.println("This is from Hello v1"); }} package com.mythsman.test;public class Hello { public void say() { System.out.println("This is from Hello v2"); }} The two class names are the same, the only difference is that the implementation of the methods is different. We first compile separately, and then rename the generated class files to Hello.class.1 and Hello.class.2.
Our goal is to create instances of these two classes in the test class.
Then we create a new test class com.mythsman.test.Main and create two custom ClassLoaders in the main function:
ClassLoader classLoader1=new ClassLoader() { @Override public Class<?> loadClass(String s) throws ClassNotFoundException { try { if (s.equals("com.mythsman.test.Hello")) { byte[] classBytes = Files.readAllBytes(Paths.get("/home/myths/Desktop/test/Hello.class.1")); return defineClass(s, classBytes, 0, classBytes.length); }else{ return super.loadClass(s); } }catch (IOException e) { throw new ClassNotFoundException(s); } }};ClassLoader classLoader2=new ClassLoader() { @Override public Class<?> loadClass(String s) throws ClassNotFoundException { try { if (s.equals("com.mythsman.test.Hello")) { byte[] classBytes = Files.readAllBytes(Paths.get("/home/myths/Desktop/test/Hello.class.2")); return defineClass(s, classBytes, 0, classBytes.length); }else{ return super.loadClass(s); } } catch (IOException e) { throw new ClassNotFoundException(s); } }};
The purpose of these two ClassLoaders is to associate two different bytecodes of the Hello class separately. We need to read the bytecode file and load it into class through the defineClass method. Note that we overload the loadClass method. If we overload the findClass method, then due to the parent delegate processing mechanism of the loadClass method, the findClass method of the second ClassLoader will not be called.
So how do we generate instances? Obviously, we cannot directly refer to class names (name conflicts), so we can only use reflection:
Object helloV1=classLoader1.loadClass("com.mythsman.test.Hello").newInstance();Object helloV2=classLoader2.loadClass("com.mythsman.test.Hello").newInstance();helloV1.getClass().getMethod("say").invoke(helloV1);helloV2.getClass().getMethod("say").invoke(helloV2); Output:
This is from Hello v1This is from Hello v2
OK, even if you have completed two loads, there are still a few points to pay attention to.
What is the relationship between two classes
Obviously, these two classes are not the same class, but their names are the same. So what are the results of operators like isinstance of:
System.out.println("class:"+helloV1.getClass());System.out.println("class:"+helloV2.getClass());System.out.println("has hCode:"+helloV2.getClass().hashCode());System.out.println("classLoader:"+helloV1.getClass().getClassLoader());System.out.println("classLoader:"+helloV2.getClass().getClassLoader()); Output:
class:class com.mythsman.test.Helloclass:class com.mythsman.test.HellohashCode:1581781576hashCode:1725154839classLoader:com.mythsman.test.Main$1@5e2de80cclassLoader:com.mythsman.test.Main$2@266474c2
Their class names are indeed the same, but the hashcodes of the class are different, which means that these two are essentially not the same class, and their class loaders are also different (in fact, they are the two internal classes of Main).
What is the relationship between these two class loaders and the three-layer class loader of the system?
Take the first custom class loader as an example:
System.out.println(classLoader1.getParent().getParent().getParent());System.out.println(classLoader1.getParent().getParent());System.out.println(classLoader1.getParent());System.out.println(classLoader1.getParent());System.out.println(classLoader1);System.out.println(ClassLoader.getSystemClassLoader());
Output:
[email protected][email protected][email protected]$AppClassLoader@18b4aac2
Of course, the parent-son relationship mentioned here is not an inheritance relationship, but a combination relationship. The child ClassLoader saves a reference (parent) of the parent ClassLoader.