Tomcat Classloader如何在同一个JVM中分离不同的Webapps对象作用域?

由于Tomcat可以同时加载多个webapp,并且这些webapp可以分开工作,并且不会互相干扰,并且它们可以在同一个JVM中工作。 所以我对tomcat如何在同一个JVM中处理Object作用域感到困惑。

例如,我在两个不同的Web应用程序中都有一个单例对象,而tomcat将为每个Web应用程序生成两个不同的单例对象。 我一直认为单例对象在同一个JVM中只有一个对象,但在tomcat JVM中可能有两个或更多。


我已经阅读了有关ClassLoader的一些信息,Tomcat有自己的WebAppClassLoader来加载webapps。 那么这意味着这里的对象范围是ClassLoader还是我错了。 有谁知道这个或者可以给我一些关于tomcat工作内存布局的信息?

所有秘密都在那些ClassLoader实例后面。

类的状态(像所有静态变量,字节代码等)由加载该类的类加载器确定范围(类由其名称和加载类的类加载器标识。这不是范围,但是考虑范围通常有助于更好地理解这一点)。

因此,如果一个类由两个不同的类加载器加载,则该类在VM中存在两次,它具有两组静态字段,可以具有不同的字节代码(如不同的方法实现)以及所有这些。 “普通”Java应用程序具有由类加载器层次结构加载的所有类,并且每个类仅加载一次。

对于更复杂的场景,您将需要不同的行为。 有时你想要隔离一个库而不是弄乱你的代码(比如eclipse中的插件或应用程序服务器中的web应用程序)。

将程序与其他类隔离的基本思想是使用额外的类加载器加载它们并使用大量的reflection。 如果您想阅读本文,请查看Oracle关于ClassLoaders或OSGI的文档。

Tomcat(以及许多其他Web容器/应用程序服务器)使用单独的ClassLoader层次结构加载应用程序。 这将所有类与其他(web)应用程序隔离开来,因此也确保单例,不同类版本和所有这些内容不会发生冲突。

请记住,Java中的类由其完全限定名称加载它的类加载器标识。 Tomcat为您部署的每个上下文(Web应用程序)使用单独的类加载器,从而将它们分开。 此外,系统类加载器加载tomcat特定库,JVM引导加载程序加载Java核心库。

在谈论单身人士时,总会被遗漏的一件事是,单身人士每个类加载器只能有一个实例。 ClassLoader限制了类可见性,因此同一个VM可以存在于同一个VM中的多个不同类加载器下。 除此之外,这允许您同时加载不同版本的jar。

这个问题: Java类加载器似乎有一些很好的链接和资源可供进一步研究。

JVM中类的“ID”由完全限定的类名和用于加载它的类加载器组成。 这意味着,如果您通过不同的类加载器加载两个具有相同名称的类,则它们被视为不同的类。

在普通的Java应用程序中,当要求类加载器加载类时,它会将请求委托给它的父类加载器,然后在父类加载器找不到所请求的类时加载它。

对于Web应用程序服务器,这略有不同 对于像tomcat这样的Web应用程序服务器中部署的每个Web应用程序,通常都有不同的类加载器。 对于Tomcat,它看起来如下 –

在此处输入图像描述

因此对于Web应用程序类加载资源按以下顺序发生 –

  1. JVM的引导类(核心java类)
  2. / WEB-INF /您的Web应用程序的类
  3. 您的Web应用程序的/WEB-INF/lib/*.jar
  4. 系统类加载器类(Tomcat / Classpath特定类)
  5. 常见的类加载器类(所有Web应用程序通用的类)

但是请注意,如果Web应用程序类加载器配置了delegate="true"那么订单将被更改 –

  1. JVM的引导类(核心java类)
  2. 系统类加载器类(Tomcat / Classpath特定类)
  3. 常见的类加载器类(所有Web应用程序通用的类)
  4. / WEB-INF /您的Web应用程序的类
  5. 您的Web应用程序的/WEB-INF/lib/*.jar

有关更多详细信息,您可以查看Apache Tomcat的类加载器HOW-TO页面。

因此,对于类加载器,单例将是单例 – 在容器/ JVM中; 作为容器/ JVM可能有多个类加载器。

  1. 在tomcat中使用不同的类加载器来分离不同的应用程序。 例如app1使用ClassLoaderA,app2使用classloaderB。
  2. 每个类都将使用自己的类加载器来加载其他类。 因此,如果ClassA.class引用ClassB.class,则ClassB需要位于ClassA或其父类的类加载器的类路径中。 例如,在app1中,从ClassLoaderA加载com.exmaple.test1。 com.exmaple.test1想要新的com.exmaple.test2()。 默认情况下,它使用自己的类加载器ClassLoaderA来加载com.exmaple.test2。 所以在com.exmaple.test1的视图中,它只能看到自己的类路径类(app1 / webapp / classes或app1 / webapp / lib)。 在app2中,它会看到不同的视图。
  3. 总而言之,学习类加载器必须了解委托模型。 而能见度是孩子可以看到的父母。 但是父母看不到孩子和兄弟姐妹看不到兄弟姐妹。 所以我们可以隔离不同的app。