当同一类存在于同一服务器上的不同应用程序中时,类装入如何工作?

我在一个应用程序服务器上运行了多个Web应用程序,每个Web应用程序WAR文件都包含同一jar文件的副本。

这是否意味着该 jar 文件中的类将在 JVM 中加载多次,对于它所在的每个 WAR 文件,将加载一次?接下来,如果我在这样的类中有一个静态同步方法,它是否仅在它所在的Web应用程序中的线程之间同步,而不是与同一类中的相同方法同步,而不是与不同WAR文件中不同jar文件中的相同方法同步?(希望这个问题有意义,如有必要,将澄清)。

如果是这种情况,我认为最好的解决方案是从每个WAR文件中删除jar文件并将其部署到服务器上的共享类路径文件夹中?


答案 1

Java 类装入器通常通过在固定序列中的一个或多个位置查找类来工作。例如,从命令行运行应用程序时,装入应用程序的类装入器首先在文件(以及引导类路径上的其他文件)中查找,然后在类路径指定的目录和 JAR 文件中查找。rt.jar

Webapp类加载在原则上是相似的,但在实践中有点复杂。对于特定的 web 应用,Web 应用的类加载器按以下顺序查找类。例如,Tomcat 6 按以下顺序查找类:

  1. JVM 的引导类
  2. 系统类装入器类(此处描述))
  3. /WEB-INF/webapp 的类
  4. /WEB-INF/lib/*webapp 的.jar
  5. $CATALINA_首页/lib
  6. $CATALINA_首页/自由/*.jar

当然,一旦类装入器找到了它正在寻找的类,它就不再看了。因此,稍后在顺序中具有相同名称的类将不会加载。

复杂的是,Web 容器的每个 web 应用都有一个类加载器,这些类加载器委托给管理公共类的其他类加载器。在实践中,这意味着某些类对于整个容器(例如 1. 和 2.)只会加载一次,而其他类可能会被不同的类加载器加载多次。

(当一个类被多次装入时,它会导致不同的对象和不同的类静态。就 JVM 而言,该类的版本是不同的类型,您不能从一个版本类型转换到另一个版本。Class

最后,Tomcat可以配置为允许单个Web应用程序“热加载”。这需要停止 Web 应用,为其创建新的类装入器,然后重新启动它。

随访

所以。。。同步静态方法不会保护对已多次装入类的共享资源的访问?

这取决于细节,但可能不会。(或者看看如果换一种方式,如果一个类实际上已经被加载了多次,那么该类的每个“加载”的方法将访问一组不同的字段。staticstatic

如果确实希望单一实例应用程序类实例由同一容器中的多个 Web 应用共享,则最简单的方法是将类放入或等效项中。但你也应该问问自己,这是否是好的系统设计。考虑合并Web应用程序,或使用请求转发等而不是共享数据结构。单例模式在Web应用程序中往往很麻烦,这种风格更是如此。$CATALINA_HOME/lib


答案 2

Java EE 应用程序服务器通常使用多个类装入器来隔离应用程序,并允许在不影响其他应用程序的情况下部署一个应用程序的新版本。

您可以在具有类装入器层次结构的 EAR 中获得诸如多个 WAR 文件和一个 EJB 文件之类的模式,每个 WAR 都有自己的模式。

正如您所描述的那样,这确实会导致重复,但这并不一定是一件坏事。这意味着您甚至可以在同一时间部署同一 JAR 的不同版本,这实际上可能是有益的,从而允许增量迁移到新版本。

一些应用程序服务器(WebSphere for exmaple)明确支持共享库概念,我确实使用它。

请注意,只是将 JAR 弹出到任意类路径中,您就有可能破坏应用服务器本身的稳定性。


推荐