Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the class loading system in Tomcat9?

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

Editor to share with you what the Tomcat9 class loading system is like, I believe most people do not know much about it, so share this article for your reference, I hope you can learn a lot after reading this article, let's go to understand it!

1. Summary

As a Web container, Tomcat supports the deployment and operation of multiple applications, so it involves the isolation and sharing of class libraries among applications. For example, different applications can use different versions of a tool class library without affecting each other, or they can share a class library.

2.JVM Parental delegation Model

There are three system-provided classloaders in JVM:

Launch the classloader Bootstrap ClassLoader: this load load is stored in the

The class library in the / lib directory, the startup class loader cannot be directly referenced by the Java program

Extended class loader Extension ClassLoader: load loading

Class libraries in the / lib/ext directory

Application class loader Application ClassLoader: load the class library specified on the user's classpath CLASSPATH. The return value of the ClassLoader.getSysteClassLoader () method is this loader. Generally, this is the default class loader in the program.

Our applications are loaded by these three types of loaders, and we can also add self-defined class loaders, and the relationship between them is shown in the figure:

The hierarchical relationship between classloaders shown in the figure becomes the parent delegate model (Parents Delegation Model) of classloaders, which requires that all classloaders should have parent loaders except the top-level startup classloader. Here, the parent-child relationship between class loaders generally does not inherit the strong coupling relationship of Inheritance, but uses the weak coupling relationship of Composition to reuse the code of the parent loader.

The parent delegation model of class loader was introduced during JDK1.2 and widely used in all subsequent Java programs, but it is not a mandatory constraint model, but a class loader implementation recommended by Java designers to developers, which is broken in Apache Tomcat in later chapters.

The working process of the parent delegation model is that if a class loader receives a request to load a class, he will not load the class himself at first, but delegate the request to the parent loader to complete it. Every level of the class loader is handled in this way, and all load requests are eventually sent to the top-level startup class loader, only when the parent loader reports that it cannot complete the load request (no class lookup). The child loader will try to load itself.

The parent delegation model is essentially a principal-agent, although it is simple, it makes the Java class have a priority level along with the class loader, such as class java.lang.Object, which is stored in rt.jar. No matter which class loader loads this class, it is finally delegated to the startup class loader. Finally, the Object class is the same class in all kinds of class loader environment in the program. In other words, if you try to write a Java class with the same name as an existing class in the rJDK basic class library, similar to java.lang.Object, you will find that although it can be compiled properly, it will never be loaded and run.

3.Tomcat9 class loading system

The classloader for a fully functional Web container needs to support the following:

The server's class library should be isolated from the Web application class library and do not affect each other.

The Java class libraries used by two Web programs on the same server are independently isolated from each other.

The Java class libraries used by two Web applications on the same server can be shared

Hot replacement (HotSwap) for JSP is supported. No server restart is required to modify JSP files.

Based on the above requirements, the following figure shows the delegation relationship of the Tomcat classloader:

The basic class loader of Common ClassLoader:Tomcat, and the class in the load path can be accessed by the Tomcat container itself and by various Webapp applications

Catalina ClassLoader:Tomcat container private class loader. The class in the load path is only visible to the Tomcat container itself, and the Webapp application is not accessible.

Shared ClassLoader: a class loader shared by various Webapp applications, and the class in the load path is visible to all webapp

Webapp ClassLoader: a private class loader for each Webapp. The class in the load path is visible only to the current webapp.

JasperLoader: record a .class file compiled by the JSP file. When the container detects that the JSP file has been modified, it replaces the current JasperLoader instance and realizes the HotSwap function of the JSP file by creating a new JSP class loader.

4. Source code analysis org.apache.catalina.startup.Bootstrap.init () / * Initialize daemon. * @ throws Exception Fatal initialization error * / public void init () throws Exception {initClassLoaders (); / / initialize the classloader Thread.currentThread () .setContextClassLoader (catalinaLoader); / / set the current thread classloader to catalinaLoader SecurityClassLoad.securityClassLoad (catalinaLoader) / / initialize / / Load our startup class and call its process () method if (log.isDebugEnabled ()) log.debug ("Loading startup class") using Java SecurityManager; Class startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.getConstructor () .newInstance () / / Set the shared extensions class loader if (log.isDebugEnabled ()) log.debug ("Setting startup class properties"); String methodName = "setParentClassLoader"; Class paramTypes [] = new Class [1]; paramTypes [0] = Class.forName ("java.lang.ClassLoader"); Object paramValues [] = new Object [1]; paramValues [0] = sharedLoader / / parentLoader Method method of WebappLoader = startupInstance.getClass (). GetMethod (methodName, paramTypes); method.invoke (startupInstance, paramValues); catalinaDaemon = startupInstance;}

CommLoader, catalinaLoader, and sharedLoader are initialized at the beginning of the Tomcat container run, and the method called is org.apache.catalina.startup.Bootstrap.init ()

InitClassLoaders initializes commLoader, catalinaLoader, and sharedLoader

Set the class loader of the current thread to catalinaLoader to achieve the purpose of loading catalinaLoader for the private class of the container itself

Do some initialization work when using Java SecurityManager

Pass the sharedLoader back as the parent of the webappLoader

Org.apache.catalina.startup.Bootstrap.initClassLoaders () private void initClassLoaders () {try {commonLoader = createClassLoader ("common", null); if (commonLoader = = null) {/ / no config file, default to this loader-we might be ina 'single' env. CommonLoader = this.getClass (). GetClassLoader ();} catalinaLoader = createClassLoader ("server", commonLoader); sharedLoader = createClassLoader ("shared", commonLoader);} catch (Throwable t) {handleThrowable (t); log.error ("Class loader creation threw exception", t); System.exit (1);}}

Initialize commLoader, catalinaLoader, and sharedLoader in the initClassLoaders method

CommLoader, catalinaLoader, and sharedLoader are all created through createClassLoader

The second parameter of the createClassLoader method is the parent class loader, so the parent class loader of catalinaLoader and sharedLoader is commLoader

Org.apache.catalina.startup.Bootstrap.createClassLoader () private ClassLoader createClassLoader (String name, ClassLoader parent) throws Exception {String value = CatalinaProperties.getProperty (name + ".loader"); if ((value = = null) | | (value.equals (")) return parent; value = replace (value); List repositories = new ArrayList (); String [] repositoryPaths = getPaths (value) For (String repository: repositoryPaths) {/ / Check for a JAR URL repository try {@ SuppressWarnings ("unused") URL url = new URL (repository); repositories.add (new Repository (repository, RepositoryType.URL)); continue } catch (MalformedURLException e) {/ / Ignore} / / Local repository if (repository.endsWith ("* .jar")) {repository = repository.substring (0, repository.length ()-"* .jar" .length ()); repositories.add (new Repository (repository, jar)) } else if (repository.endsWith (".jar")) {repositories.add (new Repository (repository, RepositoryType.JAR));} else {repositories.add (new Repository (repository, RepositoryType.DIR));}} return ClassLoaderFactory.createClassLoader (repositories, parent);}

Get the corresponding loading path of the class loader. The configuration information of the path is in conf/catalina.properties by default, or you can specify the configuration file through the environment variable catalina.config.

Class loading path supports: .jar, * .jar and directory

When no load path for the class loader is configured, the parent class loader is returned directly. CatalinaLoader and sharedLoader are not configured in Tomcat9, so both loaders are commonLoader.

The following is the default load path configuration for Tomat9's conf/catalina.properties:

Common.loader= "${catalina.base} / lib", "${catalina.base} / lib/*.jar", "${catalina.home} / lib", "${catalina.home} / lib/*.jar" server.loader=shared.loader=org.apache.catalina.core.StandardContext.startInternal () / * * Start this component and implement the requirements * of {@ link org.apache.catalina.util.LifecycleBase#startInternal ()}. * * @ exception LifecycleException if this component detects a fatal error * that prevents this component from being used * / @ Override protected synchronized void startInternal () throws LifecycleException {... Omit other code. If (getLoader () = = null) {WebappLoader webappLoader = new WebappLoader (getParentClassLoader ()); webappLoader.setDelegate (getDelegate ()); setLoader (webappLoader);}. Omit other code.}

Initialization of webappLoader takes place in StandardContext, where WebappLoader is created

Org.apache.catalina.loader.WebappLoader / / re-implement ClassLoader private String loaderClass = ParallelWebappClassLoader.class.getName (); @ Override protected void startInternal () throws LifecycleException {... Omit other code. / / Construct a class loader based on our current repositories list try {classLoader = createClassLoader (); / / create webappLoader classLoader.setResources (context.getResources ()); classLoader.setDelegate (this.delegate); / / whether parents delegate or not. Omit other code.} / * Create associated classLoader. * / private WebappClassLoaderBase createClassLoader () throws Exception {Class clazz = Class.forName (loaderClass); WebappClassLoaderBase classLoader = null; if (parentClassLoader = = null) {parentClassLoader = context.getParentClassLoader ();} Class [] argTypes = {ClassLoader.class}; Object [] args = {parentClassLoader}; Constructor constr = clazz.getConstructor (argTypes); classLoader = (WebappClassLoaderBase) constr.newInstance (args) / / reflection creates classloader return classLoader;}

Create a webappLoader by calling createClassLoader () in the startInternal () method of WebappLoader

CreateClassLoader () creates ParallelWebappClassLoader through reflection

Org.apache.catalina.loader.ParallelWebappClassLoaderpublic class ParallelWebappClassLoader extends WebappClassLoaderBase {... Omit other code.} public abstract class WebappClassLoaderBase extends URLClassLoader implements Lifecycle, InstrumentableClassLoader, WebappProperties, PermissionCheck {. Omit other code. @ Override public Class loadClass (String name, boolean resolve) throws ClassNotFoundException {synchronized (JreCompat.isGraalAvailable ()? This: getClassLoadingLock (name) {if (log.isDebugEnabled ()) log.debug ("loadClass (" + name + "," + resolve + ")); Class clazz = null; / / Log access to stopped class loader checkStateForClassLoading (name) / / (0) check the previously loaded local cache, and if the cache exists, take the class clazz = findLoadedClass0 (name) in the cache; if (clazz! = null) {if (log.isDebugEnabled ()) log.debug ("Returning class from cache") If (resolve) resolveClass (clazz); return clazz;} / / (0.1) checks the previously loaded local cache. If the cache exists, the class / / GraalVM in the cache is directly returned to null. When querying the cache, it verifies the validity of name clazz = JreCompat.isGraalAvailable ()? Null: findLoadedClass (name); if (clazz! = null) {if (log.isDebugEnabled ()) log.debug ("Returning class from cache"); if (resolve) resolveClass (clazz); return clazz } / / (0.2) load the class through the system loader and place the basic class that overrides Java SE in webapp / / the class loader here should be Bootstrap ClassLoader String resourceName = binaryNameToPath (name, false); ClassLoader javaseLoader = getJavaseClassLoader (); boolean tryLoadingFromJavaseLoader; try {URL url If (securityManager! = null) {PrivilegedAction dp = new PrivilegedJavaseGetResource (resourceName); url = AccessController.doPrivileged (dp);} else {url = javaseLoader.getResource (resourceName);} tryLoadingFromJavaseLoader = (url! = null) } catch (Throwable t) {/ / Swallow all exceptions apart from those that must be re-thrown ExceptionUtils.handleThrowable (t); / / The getResource () trick won't work for this class. We have to / / try loading it directly and accept that we might get a / / ClassNotFoundException. TryLoadingFromJavaseLoader = true;} if (tryLoadingFromJavaseLoader) {try {clazz = javaseLoader.loadClass (name); if (clazz! = null) {if (resolve) resolveClass (clazz); return clazz }} catch (ClassNotFoundException e) {/ / Ignore}} / / (0.5) SecurityManager Security check if (securityManager! = null) {int I = name.lastIndexOf ('.') If (I > = 0) {try {securityManager.checkPackageAccess (name.substring (0Magi));} catch (SecurityException se) {String error = sm.getString ("webappClassLoader.restrictedPackage", name); log.info (error, se) Throw new ClassNotFoundException (error, se);} boolean delegateLoad = delegate | | filter (name, true) / / (1) when the parent delegate or load the class whose name is inside the Tomcat container, delegate to the parent class loader to load if (delegateLoad) {if (log.isDebugEnabled ()) log.debug ("Delegating to parent classloader1" + parent) Try {clazz = Class.forName (name, false, parent); if (clazz! = null) {if (log.isDebugEnabled ()) log.debug ("Loading class from parent") If (resolve) resolveClass (clazz); return clazz }} catch (ClassNotFoundException e) {/ / Ignore}} / / (2) load if (log.isDebugEnabled ()) log.debug ("Searching local repositories") in the application class loading path Try {clazz = findClass (name); if (clazz! = null) {if (log.isDebugEnabled ()) log.debug ("Loading class from local repository"); if (resolve) resolveClass (clazz); return clazz }} catch (ClassNotFoundException e) {/ / Ignore} / / (3) delegate the parent class loader to load if (! delegateLoad) {if (log.isDebugEnabled ()) log.debug ("Delegating to parent classloader at end:" + parent) Try {clazz = Class.forName (name, false, parent); if (clazz! = null) {if (log.isDebugEnabled ()) log.debug ("Loading class from parent") If (resolve) resolveClass (clazz); return clazz;}} catch (ClassNotFoundException e) {/ / Ignore} throw new ClassNotFoundException (name);} Omit other code.}

ParallelWebappClassLoader inherits WebappClassLoaderBase, while WebappClassLoaderBase inherits URLClassLoader and overrides the loadClass () method, which is equivalent to implementing its own class loader

The loadClass () method shows that the Tomcat class loading system breaks the parental delegation model, and the loading process is

Query from the cache, and if it has been loaded, directly return the class in the cache

Overwriting JavaSE base classes in applications has been avoided through system class loader loading (Bootstrap ClassLoader)

To determine whether the classname is set to parent delegation, or if the parent is under a specific path, delegate to the parent loader inclusion

Loading through the application class loading path, loading through WebResourceRoot

When the parent delegate is marked as false, it is finally delegated to the parent class loader to load

The name of the class loaded by the parent class must be judged by the filter () method:

Protected boolean filter (String name, boolean isClassName) {if (name = = null) return false; char ch; if (name.startsWith ("javax")) {/ * 5 = = length ("javax") * / if (name.length () = = 5) {return false;} ch = name.charAt (5) If (isClassName & & ch = ='.') {/ * 6 = = length ("javax.") * / if (name.startsWith ("servlet.jsp.jstl.", 6)) {return false } if (name.startsWith ("el.", 6) | | name.startsWith ("servlet.", 6) | | name.startsWith ("websocket.", 6) | | name.startsWith ("security.auth.message.", 6)) {return true }} else if (! isClassName & & ch = ='/') {/ * 6 = = length ("javax/") * / if (name.startsWith ("servlet/jsp/jstl/", 6)) {return false } if (name.startsWith ("el/", 6) | | name.startsWith ("servlet/", 6) | | name.startsWith ("websocket/", 6) | | name.startsWith ("security/auth/message/", 6)) {return true } else if (name.startsWith ("org")) {/ * 3 = = length ("org") * / if (name.length () = = 3) {return false;} ch = name.charAt (3) If (isClassName & & ch = ='.) {/ * 4 = = length ("org.") * / if (name.startsWith ("apache.", 4)) {/ * 11 = = length ("org.apache.") * / if ("tomcat.jdbc." 11)) {return false } if (name.startsWith ("el.", 11) | | name.startsWith ("catalina.", 11) | | name.startsWith ("jasper.", 11) | | name.startsWith ("juli.", 11) | | name.startsWith ("tomcat." 11) | | name.startsWith ("naming.", 11) | | name.startsWith ("coyote.", 11)) {return true } else if (! isClassName & & ch = ='/') {/ * 4 = = length ("org/") * / if (name.startsWith ("apache/") 4)) {/ * 11 = = length ("org/apache/") * / if (name.startsWith ("tomcat/jdbc/", 11)) {return false } if (name.startsWith ("el/", 11) | | name.startsWith ("catalina/", 11) | | name.startsWith ("jasper/", 11) | | name.startsWith ("juli/", 11) | | name.startsWith ("tomcat/") 11) | | name.startsWith ("naming/", 11) | | name.startsWith ("coyote/", 11)) {return true } return false;} org.apache.catalina.webresources.StandardRootpublic class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot {... Omit other code. Private final List preResources = new ArrayList (); private WebResourceSet main; private final List classResources = new ArrayList (); private final List jarResources = new ArrayList (); / / corresponding WEB-INF/lib private final List postResources = new ArrayList (); private final List mainResources = new ArrayList (); / / corresponding application webapp path private final List allResources = new ArrayList (); {allResources.add (preResources); allResources.add (mainResources); allResources.add (classResources) AllResources.add (jarResources); allResources.add (postResources);}. Omit other code.}

StandardRoot is initialized when the container is created, which implements the interface WebResourceRoot, which is used to load the Webapp class library.

As you can see from the above code, the loading order of the Webapp class in Tomcat is:

PreResources- > mainResources- > classResources- > jarResources- > postResources

The main resource paths used are:

MainResources: WEB-INF/classes under the corresponding application directory

ClassResources: WEB-INF/lib under the corresponding application directory

So when developing, you can override the classes in the lib package under the src directory, because WEB-INF/classes will be loaded by a limited WEB-INF/lib. Usually we can copy the source code of dependent third-party class libraries to the src directory and run debug through Tomcat, which is very helpful for troubleshooting problems with third-party class libraries.

Org.apache.jasper.JspCompilationContextpublic ClassLoader getJspLoader () {if (jspLoader = = null) {jspLoader = new JasperLoader (new URL [] {baseUrl}, getClassLoader (), rctxt.getPermissionCollection ());} return jspLoader;} public void clearJspLoader () {jspLoader = null;}

In the conf/web.xml of Tomcat, the Servlet:org.apache.jasper.servlet.JspServlet that handles the JSP is specified. When processing the request for the JSP page, Tomcat will detect whether the corresponding JSP file has been modified, and if the change occurs, it will clean up the compiled file of JSP, and then convert it into a java file, which will be compiled into a class file.

JspLoader is a class loader that loads JSP converted into Servlet, and will be regenerated when Tomcat detects a change in the jsp file.

The parent class loader of jspLoader is the classloader of webapp, and the initial of the parent class loader is in the construction method of org.apache.jasper.compiler.JspRuntimeContext, as shown in the following code

Public JspRuntimeContext (ServletContext context, Options options) {.. Omit the other code / / Get the parent class loader ClassLoader loader = Thread.currentThread (). GetContextClassLoader (); if (loader = = null) {loader = this.getClass (). GetClassLoader ();} parentClassLoader = loader;. Omit other code.} above is all the content of this article "what is the class loading system in Tomcat9?" Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report