33

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

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

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

4

3 回答 3

38

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

webapp 类加载在原理上是类似的,但在实践中要复杂一些。对于特定的 webapp,webapp 的类加载器按以下顺序查找类。例如,Tomcat 6 按以下顺序查找类:

  1. JVM 的引导类
  2. 系统类加载器类(描述here
  3. /WEB-INF/webapp 的类
  4. /WEB-INF/lib/*.jar webapp
  5. $CATALINA_HOME/lib
  6. $CATALINA_HOME/lib/*.jar

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

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

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

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

跟进

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

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

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

于 2010-11-09T10:36:51.997 回答
5

Java EE 应用程序服务器通常使用多个类加载器将应用程序彼此隔离,并允许部署一个应用程序的新版本而不影响其他应用程序。

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

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

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

谨防将 JAR 放入任意类路径中,否则可能会破坏应用服务器本身的稳定性。

于 2010-11-09T10:26:24.027 回答
0

大多数应用服务器使用最具体的沿路径优先策略。如果你有多个库做同样的事情,你应该考虑把它们放在应用程序服务器库中(fe:TOMCAT_HOME/lib)

于 2010-11-09T10:23:17.573 回答