In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)06/01 Report--
How to implement class isolation loading in Java? Many novices are not very clear about this. In order to help you solve this problem, the following editor will explain it in detail. People with this need can come and learn. I hope you can gain something.
What is class isolation technology?
As long as you write enough Java code, this situation is bound to happen: the system introduces a new jar package of middleware, which compiles normally and reports an error as soon as it runs: java.lang.NoSuchMethodError, and then starts to wheeze to find a solution, and finally finds the conflicting jar only when it is almost blind in hundreds of dependent packages. After solving the problem, it begins to complain about why the middleware makes so many different versions of jar. Write the code for five minutes and arrange the bags all day.
The above situation is a common situation in the Java development process, and the reason is very simple. Different jar packages rely on different versions of some general jar packages (such as logging components). There is no problem at compile time, and an error will be reported at run time because the loaded classes do not match the expectations. For example, An and B rely on the v1 and v2 versions of C respectively, and the v2 version of Log adds the error method to the v1 version. Now both An and B jar packages and C v0.1 and v0.2 versions are introduced in the project. When packaging, maven can only choose one C version, assuming that v1 version is selected. At run time, all classes of a project are loaded with the same classloader by default, so no matter how many versions of C you rely on, only one version of C will be loaded into JVM. When B is going to visit Log.error, he will find that Log has no error method at all, and then throws an exception java.lang.NoSuchMethodError. This is a typical case of class conflict.
The problem of class conflicts is easy to solve if the version is backward compatible, and it's over to rule out the lower version. But if the version is not backward compatible, it will be caught in the dilemma of "saving your mother or saving your girlfriend".
In order to avoid a dilemma, some people put forward the technology of class isolation to solve the problem of class conflict. The principle of class isolation is also simple: each module is loaded with a separate class loader so that the dependencies between different modules do not affect each other. As shown in the following figure, different modules are loaded with different class loaders. Why does this resolve class conflicts? A mechanism of Java is used here: classes loaded by different loaders are two different classes in JVM's view, because the unique identity of a class in JVM is class loader + class name. In this way we can load two different versions of the class of C at the same time, even if the class name is the same. Note that classloaders refer to instances of classloaders, and it is not necessary to define two different loaders. For example, PluginClassLoaderA and PluginClassLoaderB in the figure can be different instances of the same classloader.
Second, how to realize class isolation
We mentioned earlier that class isolation is about having jar packages of different modules loaded with different class loaders. To do this, you need to have JVM load the classes we write and their associated classes using a custom class loader.
So how to achieve it? A very simple approach is that JVM provides a setting interface for the global class loader, so we can directly replace the global class loader, but this does not solve the problem of multiple custom class loaders at the same time.
In fact, JVM provides a very simple and efficient way, which I call the class load conduction rule: JVM selects the class loader of the current class to load all classes referenced by that class. For example, we define two classes, TestA and TestB, and TestA will refer to TestB. As long as we use a custom class loader to load TestA, then at run time, when TestA is called to TestB, TestB will also be loaded by JVM's classloader using TestA. And so on, classes that are all jar packages associated with TestA and its reference classes are loaded by the custom class loader. In this way, as long as we have the module's main method class loaded with a different class loader, then each module's will be loaded with the main method class's class loader, so that multiple modules can use different class loaders. This is also the core principle that OSGi and SofaArk can achieve class isolation.
After understanding the implementation principle of class isolation, we start with rewriting the class loader. To implement your own class loader, first let the custom class loader inherit java.lang.ClassLoader, and then override the class loading method. Here we have two choices, one is to rewrite findClass (String name), and the other is to override loadClass (String name). So which one should I choose? What's the difference between the two?
Let's try to override these two methods respectively to implement a custom class loader.
1 rewrite findClass
First of all, we define two classes. TestA will print its own classloader, and then call TestB to print its classloader. We expect that the classloader MyClassLoaderParentFirst, which overrides the findClass method, will be able to load TestB automatically by MyClassLoaderParentFirst after loading TestA.
Public class TestA {public static void main (String [] args) {TestA testA = new TestA (); testA.hello ();} public void hello () {System.out.println ("TestA:" + this.getClass (). GetClassLoader ()); TestB testB = new TestB (); testB.hello () }} public class TestB {public void hello () {System.out.println ("TestB:" + this.getClass (). GetClassLoader ());}}
Then rewrite the findClass method, which loads the class file based on the file path, and then calls defineClass to get the Class object.
Public class MyClassLoaderParentFirst extends ClassLoader {private Map classPathMap = new HashMap (); public MyClassLoaderParentFirst () {classPathMap.put ("com.java.loader.TestA", "/ Users/hansong/IdeaProjects/OhMyJava/CodeRepository/target/classes/com/java/loader/TestA.class"); classPathMap.put ("com.java.loader.TestB", "/ Users/hansong/IdeaProjects/OhMyJava/CodeRepository/target/classes/com/java/loader/TestB.class") } / / overridden findClass method @ Override public Class findClass (String name) throws ClassNotFoundException {String classPath = classPathMap.get (name); File file = new File (classPath); if (! file.exists ()) {throw new ClassNotFoundException ();} byte [] classBytes = getClassData (file); if (classBytes = = null | classBytes.length = = 0) {throw new ClassNotFoundException () } return defineClass (classBytes, 0, classBytes.length);} private byte [] getClassData (File file) {try (InputStream ins = new FileInputStream (file); ByteArrayOutputStream baos = new ByteArrayOutputStream ()) {byte [] buffer = new byte [4096]; int bytesNumRead = 0 While ((bytesNumRead = ins.read (buffer))! =-1) {baos.write (buffer, 0, bytesNumRead);} return baos.toByteArray ();} catch (FileNotFoundException e) {e.printStackTrace ();} catch (IOException e) {e.printStackTrace ();} return new byte [] {} }}
Finally, write a main method to call the custom classloader to load the TestA, and then print the information of the classloader through reflection calling the main method of TestA.
Public class MyTest {public static void main (String [] args) throws Exception {MyClassLoaderParentFirst myClassLoaderParentFirst = new MyClassLoaderParentFirst (); Class testAClass = myClassLoaderParentFirst.findClass ("com.java.loader.TestA"); Method mainMethod = testAClass.getDeclaredMethod ("main", String [] .class); mainMethod.invoke (null, new Object [] {args});}
The results of the execution are as follows:
TestA: com.java.loader.MyClassLoaderParentFirst@1d44bcfaTestB: sun.misc.Launcher$AppClassLoader@18b4aac2
The result of the execution is not as we expected. TestA is indeed loaded by MyClassLoaderParentFirst, but TestB is still loaded by AppClassLoader. Why is that?
To answer this question, the first step is to understand a rule for class loading: JVM calls the ClassLoader.loadClass method when it triggers class loading. This method implements parental delegation:
Delegate to the parent loader query
If the parent loader cannot query it, call the findClass method to load
After understanding this rule, the reason for the result of the execution is found: JVM does use MyClassLoaderParentFirst to load TestB, but because of the parent delegation mechanism, TestB is delegated to MyClassLoaderParentFirst's parent loader AppClassLoader to load.
You may also wonder why MyClassLoaderParentFirst's parent loader is AppClassLoader. Because the main method classes we define are loaded by the AppClassLoader that comes with JDK by default, according to the class load conduction rules, the MyClassLoaderParentFirst referenced by the main class is also loaded by the AppClassLoader that loads the main class. Because the parent class of MyClassLoaderParentFirst is the default constructor of ClassLoader,ClassLoader, the parent loader's value is automatically set to AppClassLoader.
Protected ClassLoader () {this (checkCreateClassLoader (), getSystemClassLoader ();)
2 rewrite loadClass
Because rewriting the findClass method will be affected by the parent delegation mechanism, the TestB will be loaded by AppClassLoader, which does not meet the goal of class isolation, so we can only rewrite the loadClass method to destroy the parent delegation mechanism. The code is as follows:
Public class MyClassLoaderCustom extends ClassLoader {private ClassLoader jdkClassLoader; private Map classPathMap = new HashMap (); public MyClassLoaderCustom (ClassLoader jdkClassLoader) {this.jdkClassLoader = jdkClassLoader; classPathMap.put ("com.java.loader.TestA", "/ Users/hansong/IdeaProjects/OhMyJava/CodeRepository/target/classes/com/java/loader/TestA.class") ClassPathMap.put ("com.java.loader.TestB", "/ Users/hansong/IdeaProjects/OhMyJava/CodeRepository/target/classes/com/java/loader/TestB.class");} @ Override protected Class loadClass (String name, boolean resolve) throws ClassNotFoundException {Class result = null; try {/ / here you want to use JDK's classloader to load the class result = jdkClassLoader.loadClass (name) in the java.lang package } catch (Exception e) {/ / ignore} if (result! = null) {return result;} String classPath = classPathMap.get (name); File file = new File (classPath); if (! file.exists ()) {throw new ClassNotFoundException ();} byte [] classBytes = getClassData (file) If (classBytes = = null | | classBytes.length = = 0) {throw new ClassNotFoundException ();} return defineClass (classBytes, 0, classBytes.length);} private byte [] getClassData (File file) {/ / omitted}}
Note here that we rewrote the loadClass method, which means that all classes, including those in the java.lang package, will be loaded through MyClassLoaderCustom, but the goal of class isolation does not include the classes that come with this part of JDK, so we use ExtClassLoader to load JDK classes, the related code is: result = jdkClassLoader.loadClass (name)
The test code is as follows:
Public class MyTest {public static void main (String [] args) throws Exception {/ / take the parent loader of AppClassLoader, that is, ExtClassLoader as MyClassLoaderCustom's jdkClassLoader MyClassLoaderCustom myClassLoaderCustom = new MyClassLoaderCustom (Thread.currentThread (). GetContextClassLoader (). GetParent ()); Class testAClass = myClassLoaderCustom.loadClass ("com.java.loader.TestA"); Method mainMethod = testAClass.getDeclaredMethod ("main", String [] .class); mainMethod.invoke (null, new Object [] {args}) }}
The implementation results are as follows:
TestA: com.java.loader.MyClassLoaderCustom@1d44bcfaTestB: com.java.loader.MyClassLoaderCustom@1d44bcfa, is it helpful for you to read the above content? If you want to know more about the relevant knowledge or read more related articles, please follow the industry information channel, thank you for your support.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.