In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
How to deeply explore the Groovy ClassLoader system, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain for you in detail, people with this need can come to learn, I hope you can gain something.
There are a lot of ClassLoader defined in Groovy, most of which are involved in Groovy scripts, and the three most important ones are ClassLoader:RootLoader, GroovyClassLoader, and GroovyClassLoader.InnerLoader.
Note: the Groovy source code for the following analysis is from Groovy 2.1.3.
ClassLoader of Java
As the name implies, Java's ClassLoader is the class loader, which enables JVM to load Java classes dynamically. JVM does not need to know where to load Java classes (local files, networks, etc.), all done by ClassLoader.
It can be said that ClassLoader is the namespace of Class. Classes with the same name can be loaded by multiple ClassLoader, classes with the same name loaded by different ClassLoader will be considered different classes, while the same ClassLoader can only load classes with the same name once.
Java's ClassLoader has a famous parental delegation model (Parent Delegation Model): in addition to Bootstrap ClassLoader, each ClassLoader has a parent ClassLoader, which will eventually be traced to Bootstrap ClassLoader; along the parent. When a ClassLoader wants to load a class, it will first be delegated to parent, if parent can load the class, it will return, otherwise the ClassLoader will try to load the class.
The ClassLoader system of Java is as follows, where the arrow points to the parent of the ClassLoader:
Bootstrap ClassLoader ↑ Extension ClassLoader ↑ System ClassLoader ↑ User Custom ClassLoader / / not necessarily
ClassLoader of Groovy
Let's first look at a script to see what the ClassLoader of a Groovy script and its ancestors are:
Def cl = this.class.classLoader while (cl) {println cl cl = cl.parent}
The output is as follows:
Groovy.lang.GroovyClassLoader$InnerLoader@18622f3 groovy.lang.GroovyClassLoader@147c1db org.codehaus.groovy.tools.RootLoader@186db54 sun.misc.Launcher$AppClassLoader@192d342 sun.misc.Launcher$ExtClassLoader@6b97fd
As a result, we get the ClassLoader system of Groovy:
Null / / that is Bootstrap ClassLoader ↑ sun.misc.Launcher.ExtClassLoader / / that is Extension ClassLoader ↑ sun.misc.Launcher.AppClassLoader / / that is System ClassLoader ↑ org.codehaus.groovy.tools.RootLoader / / the following is User Custom ClassLoader ↑ groovy.lang.GroovyClassLoader ↑ groovy.lang.GroovyClassLoader.InnerLoader
Let's introduce RootLoader, GroovyClassLoader, and GroovyClassLoader.InnerLoader, respectively.
Groovy script startup process
Before we introduce RootLoader, we need to introduce the startup process of the Groovy script.
When we type "groovy SomeScript" on the command line to run the script, we call the shell script $GROOVY_HOME/bin/groovy:
#... StartGroovy groovy.ui.GroovyMain "$@"
Where startGroovy is defined in $GROOVY_HOME/bin/startGroovy:
#... STARTER_CLASSPATH= "$GROOVY_HOME/lib/groovy-2.1.3.jar" #... StartGroovy () {CLASS=$1 shift # Start the Profiler or the JVM if $useprofiler Then runProfiler else exec "$JAVACMD" $JAVA_OPTS\-classpath "$STARTER_CLASSPATH"\-Dscript.name= "$SCRIPT_PATH"\-Dprogram.name= "$PROGNAME"\-Dgroovy.starter.conf= "$GROOVY_CONF"\-Dgroovy.home= "$GROOVY_HOME"\-Dtools.jar= "$TOOLS_JAR"\ $STARTER_MAIN_CLASS\-- main $CLASS\-- conf "$GROOVY_CONF"\-- classpath "$CP"\ "$@" fi} STARTER_MAIN_CLASS=org.codehaus.groovy.tools.GroovyStarter
We can see that here you actually start org.codehaus.groovy.tools.GroovyStarter through java, and then pass "--main groovy.ui.GroovyMain" as an argument to GroovyStarter,*** and SomeScript as an argument to GroovyMain. Notice that only $GROOVY_HOME/lib/groovy-2.1.3.jar is passed to JVM as a classpath parameter and does not include the third-party jar package that Groovy depends on.
Let's take a look at the source code of GroovyStarter (which omits the code for exception handling):
Public static void rootLoader (String args []) {String conf = System.getProperty ("groovy.starter.conf", null); LoaderConfiguration lc = new LoaderConfiguration (); / / omit the code for parsing command line arguments / / load configuration file if (confessing arguments null) {lc.configure (new FileInputStream (conf));} / / create loader and execute main class ClassLoader loader = new RootLoader (lc) Class c = loader.loadClass (lc.getMainClass ()); / / use RootLoader to load GroovyMain Method m = c.getMethod ("main", new Class [] {String [] .class}); m.invoke (null, new Object [] {newArgs}); / / call GroovyMain's main method} / / public static void main (String args []) {rootLoader (args);}
What is the LoaderConfiguration used for here? It is used to parse the $GROOVY_HOME/conf/groovy-starter.conf file, which contains the following (without comments):
Load! {groovy.home} / lib/*.jar load! {user.home} / .groovy/lib/*.jar load ${tools.jar}
This means that by adding $GROOVY_HOME/lib/*.jar, $HOME/.groovy/lib/*.jar, and tools.jar to RootLoader's classpath, you can see that there are third-party jar packages that Groovy depends on.
Next, let's look at the source code of GroovyMain. After entering the main function of GroovyMain, it will eventually reach the processOnce method:
Private void processOnce () throws CompilationFailedException, IOException {GroovyShell groovy = new GroovyShell (conf); if (isScriptFile) {if (isScriptUrl (script)) {groovy.run (getText (script), script.substring (script.lastIndexOf ("/") + 1), args);} else {groovy.run (huntForTheScriptFile (script), args) / / the local script file executes this line}} else {groovy.run (script, "script_from_command_line", args);}}
As you can see, GroovyMain executes the script file through GroovyShell. We will not analyze the specific code for executing the script in GroovyShell, but we will only look at the code that initializes ClassLoader in the constructor of GroovyShell:
Final ClassLoader parentLoader = (parentless null)? parent:GroovyShell.class.getClassLoader (); this.loader = AccessController.doPrivileged (new PrivilegedAction () {public GroovyClassLoader run () {return new GroovyClassLoader (parentLoader,config);}})
Thus, GroovyShell uses GroovyClassLoader to load the class, and the parent of this GroovyClassLoader is the ClassLoader of GroovyShell, the ClassLoader of GroovyMain, that is, RootLoader.
* to summarize the startup process of Groovy script (the ClassLoader used in parentheses):
GroovyStarter ↓ (RootLoader) GroovyMain ↓ GroovyShell ↓ (GroovyClassLoader) SomeScript
RootLoader
RootLoader, as the root ClassLoader of Groovy, is responsible for loading classes from Groovy and its dependent third-party libraries. It manages the classpath of Groovy, to which we can add a path through the $GROOVY_HOME/conf/groovy-starter.conf file or the command-line argument "- classpath" of groovy. Note that this is different from the classpath in classpath,RootLoader defined by java's command-line argument "- classpath" that is not visible to Java's original ClassLoader.
Let's take a look at how RootLoader is represented as the classpath manager of Groovy through a script:
Class C {} println this.class.classLoader println C.classLoader println () println groovy.ui.GroovyMain.classLoader println org.objectweb.asm.ClassVisitor.classLoader println () println String.classLoader println () println org.codehaus.groovy.tools.GroovyStarter.classLoader println ClassLoader.systemClassLoader.findLoadedClass ('org.codehaus.groovy.tools.GroovyStarter')? .classLoader println ()
The output is as follows:
Groovy.lang.GroovyClassLoader$InnerLoader@1ba6076 groovy.lang.GroovyClassLoader$InnerLoader@1ba6076 org.codehaus.groovy.tools.RootLoader@a97b0b org.codehaus.groovy.tools.RootLoader@a97b0b null org.codehaus.groovy.tools.RootLoader@a97b0b sun.misc.Launcher$AppClassLoader@192d342
The ClassLoader for script class and class C is GroovyClassLoader.InnerLoader, which is what we expected.
GroovyMain's ClassLoader is RootLoader because GroovyStarter uses RootLoader to load it; and ClassVisitor is a class in the asm library that Groovy depends on, and the jar file path of this library is not in Java's classpath, but in Groovy's classpath, so naturally, its ClassLoader is also RootLoader.
The ClassLoader for String is null, because all the basic types in JDK must be loaded by Bootstrap ClassLoader (it would be a mess if custom ClassLoader was allowed to load).
GroovyStarter's ClassLoader is RootLoader, which comes as a surprise. GroovyStarter should already be loaded by System ClassLoader (systemClassLoader.findLoadedClass confirms this idea). According to the parent delegation model, no descendant of System ClassLoader will try to load this class, so why did RootLoader load GroovyStarter again?
The answer is simple because RootLoader does not follow the parental delegation model. Let's take a look at RootLoader's loadClass method (doing some simple expansion):
Protected synchronized Class loadClass (final String name, boolean resolve) throws ClassNotFoundException {Class c = this.findLoadedClass (name); if (c! = null) return c; c = (Class) customClasses.get (name); / / customClasses defines some classes if (c! = null) return c; try {c = super.findClass (name) that must be loaded by Java's original ClassLoader. / / first try to load this class} catch (ClassNotFoundException cnfe) {/ / IGNORE} if (c = = null) c = super.loadClass (name, resolve); / / if not loaded, return to the original parent delegation model if (resolve) resolveClass (c); return c;}
RootLoader first tries to load the class, and if it fails to load, it delegates to parent to load, so even if parent has already loaded GroovyStarter,RootLoader, it will load again.
Why are you doing this? The reason is simple. In the previous article, I repeatedly reminded you that Java's classpath contains only Groovy's jar package, not the third-party jar package that Groovy depends on, while Groovy's classpath contains Groovy and all the third-party jar packages it depends on. If RootLoader uses the parent delegation model, then the classes in the jar package of Groovy will be loaded by System ClassLoader. When parsing the class of Groovy, you need to load the third-party jar package, and System ClassLoader does not know where to load, so the class cannot be found. Therefore, RootLoader does not use the parent delegation model.
You may be wondering: why not add all these jar packages to Java's classpath? Wouldn't there be such a problem? That's true, but Groovy can add paths to its classpath more flexibly in a variety of ways (you can even add paths to RootLoader's classpath through code), while Java's classpath can only be added from the command line, so there is a design like RootLoader.
GroovyClassLoader
GroovyClassLoader is mainly responsible for compiling groovy source code to Class at run time, so that Groovy can dynamically load groovy source code into Class.
The important work of GroovyClassLoader compiling groovy code focuses on the doParseClass method:
Private Class doParseClass (GroovyCodeSource codeSource) {validate (codeSource); / / simply verify whether some parameters are null Class answer; / / Was neither already loaded nor compiling, so compile and add to cache. CompilationUnit unit = createCompilationUnit (config, codeSource.getCodeSource ()); SourceUnit su = null; if (codeSource.getFile () = = null) {su = unit.addSource (codeSource.getName (), codeSource.getScriptText ());} else {su = unit.addSource (codeSource.getFile ());} ClassCollector collector = createCollector (unit, su); / / InnerLoader unit.setClassgenCallback (collector) is created here Int goalPhase = Phases.CLASS_GENERATION; if (config! = null & & config.getTargetDirectory ()! = null) goalPhase = Phases.OUTPUT; unit.compile (goalPhase); / / compile the groovy source code / / find MainClass answer = collector.generatedClass; String mainClass = su.getAST (). GetMainClassName () in the source file; for (Object o: collector.getLoadedClasses ()) {Class clazz = (Class) o String clazzName = clazz.getName (); definePackage (clazzName); setClassCacheEntry (clazz); if (clazzName.equals (mainClass)) answer = clazz;} return answer;}
How to compile the groovy source code is beyond the scope of this article, so I will not cover the specific process.
GroovyClassLoader.InnerLoader
Let's move on to GroovyClassLoader's createCollector method:
Protected ClassCollector createCollector (CompilationUnit unit, SourceUnit su) {InnerLoader loader = AccessController.doPrivileged (new PrivilegedAction () {public InnerLoader run () {return new InnerLoader (GroovyClassLoader.this);}}); return new ClassCollector (loader, unit, su);} public static class ClassCollector extends CompilationUnit.ClassgenCallback {private final GroovyClassLoader cl; / / … Protected ClassCollector (InnerLoader cl, CompilationUnit unit, SourceUnit su) {this.cl = cl; / /. } public GroovyClassLoader getDefiningClassLoader () {return cl;} protected Class createClass (byte [] code, ClassNode classNode) {GroovyClassLoader cl = getDefiningClassLoader (); Class theClass = cl.defineClass (classNode.getName (), code, 0, code.length, unit.getAST (). GetCodeSource ()); / / load this class this.loadedClasses.add (theClass) via InnerLoader; / /... Return theClass;} / /. }
We can see that the function of ClassCollector is to load the compiled bytecode through InnerLoader during the compilation process. In addition, each time the groovy source code is compiled, a new instance of InnerLoader is created.
How does InnerLoader load these classes? It delegates all the loading work back to GroovyClassLoader. As the InnerLoader code is simple, it will not be posted here.
Then why do you need InnerLoader when you have GroovyClassLoader? There are two main reasons:
Since a ClassLoader can only be loaded once for a class with the same name, if it is all loaded by GroovyClassLoader, then when a C class is defined in one script and a C class is defined in another script, GroovyClassLoader cannot be loaded.
Because when a class's ClassLoader is GC, the class can be GC, if all classes are loaded by GroovyClassLoader, then only when GroovyClassLoader is GC, all these classes can be GC, and if you use InnerLoader, because after compiling the source code, there is no external reference to it, except for the loaded class, so as long as the loaded class is not referenced, it and its loaded classes can be GC.
Summary
This paper introduces the three main ClassLoader in Groovy:
RootLoader: classpath that manages Groovy and is responsible for loading classes from Groovy and its dependent third-party libraries, which does not use the parent delegation model.
GroovyClassLoader: responsible for compiling the groovy source code to Class at run time, thus enabling Groovy to dynamically load the groovy source code into Class.
The direct ClassLoader of the GroovyClassLoader.InnerLoader:Groovy script class, which delegates the loading work to GroovyClassLoader, it exists to support the use of the same class name in different source code, and the loaded class can be successfully GC.
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.