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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4132545/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 11:57:08  来源:igfitidea点击:

How does class loading work when the same class exists in different applications on the same server?

javajakarta-eeclassloaderwar-filedeployment

提问by CodeClimber

I have multiple web-apps running on an app server and each web-app WAR file contains a copy of the same jar file.

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

Does this mean that a class in that jar file will be loaded multiple times in the JVM, once for each WAR file it exists in? Following on from that, if I have a static synchronized method in such a class, is it only synchronized among threads within the web-app it exists in but not synchronized against the same method in the same class in a different jar file in a different WAR file? (Hope the question makes sense, will clarify if necessary).

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

If this is the case I presume the best solution is to remove the jar file from each WAR file and deploy it to a shared classpath folder on the server?

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

采纳答案by Stephen C

A Java classloader typically works by looking for classes in one or more places in a fixed sequence. For instance, the classloader that loads your application when you run it from the command line looks first in the rt.jarfile (and others on the bootclasspath), and then in the directories and JAR files specified by your classpath.

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

A webapp classloading is similar in principle, but a bit more complicated in practice. For a particular webapp, a webapp's classloader looks for classes in the following order. For example Tomcat 6 looks for classes in this order:

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

  1. Bootstrap classes of your JVM
  2. System class loader classes (described here)
  3. /WEB-INF/classes of the webapp
  4. /WEB-INF/lib/*.jar of the webapp
  5. $CATALINA_HOME/lib
  6. $CATALINA_HOME/lib/*.jar
  1. JVM 的引导类
  2. 系统类加载器类(在此处描述)
  3. /WEB-INF/webapp 的类
  4. webapp的/WEB-INF/lib/*.jar
  5. $CATALINA_HOME/lib
  6. $CATALINA_HOME/lib/*.jar

Of course, once the classloader has found the class it is looking for, it looks no further. So classes with the same name later in the order won't get loaded.

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

The complication is that the web container has one classloader for each webapp, and these classloaders delegate to other classloaders that manage the common classes. In practice, this means that some classes will only ever be loaded once for the entire container (e.g. 1. and 2.) and others may get loaded multiple times by different classloaders.

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

(When a class is loaded more than once, it results in distinct Classobjects and distinct class statics. The versions of the class are different types as far as the JVM is concerned and you cannot typecast from one version to the other.)

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

Finally, Tomcat can be configure to allow individual webapps to be "hot loaded". This entails stopping a webapp, creating a new classloader for it, and restarting it.

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

FOLLOWUP

跟进

So ... synchronizing a static method will not protect access to a shared resource where the class has been loaded multiple times?

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

It depends on the details, but it probably won't. (Or to look at if another way, if a class has actuallybeen loaded multiple times, then a staticmethod of each "load" of the class will access a different set of staticfields.)

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

If you really want a singleton application class instance to be shared by multiple webapps in the same container, it is simplest if you put the class into $CATALINA_HOME/libor the equivalent. But you also should ask yourself if this is good system design. Consider combining the webapps, or to using request forwarding etc instead of a shared data structure. The singleton pattern tends to be troublesome in webapps, and this flavor is even more so.

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

回答by Maurizio Cucchiara

Most application server use most specific along a path takes precedencepolicy. If you have multiple library that do the same thing, You should consider to put them inside application server lib (f.e: TOMCAT_HOME/lib)

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

回答by djna

Java EE application servers typically use multiple classloaders to isolate applications from each other, and allow new versions of one application to be deployed without affecting other apps.

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

You get patterns such as several WAR files and one EJB file in an EAR with a hierarchy of classloaders, each WAR having it's own.

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

This does lead to duplication as you describe, but this is not necesserily a bad thing. It means that you can even have different versions of the same JARs deployed a the same time, and that may actually be benficial, allowing incremental migration to new versions.

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

Some application servers (WebSphere for exmaple) have explicit support for a shared library concept, and I do use that.

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

Be wary of just popping JARs into arbitrary classpaths, you run the risk of destabilising the app server itself.

小心只是将 JAR 弹出到任意类路径中,你会冒着破坏应用服务器本身稳定性的风险。