我在应用服务器上运行多个网络应用,每个网络应用WAR文件都包含同一个jar文件的副本。
这是否意味着该jar文件中的一个类将在JVM中多次加载,对于它存在的每个WAR文件一次?接下来,如果我在这样的类中有一个静态同步方法,它是否只在它存在的web-app中的线程之间同步,而不是在不同jar文件中的同一个类中的同一方法同步WAR文件? (希望这个问题有道理,必要时会澄清)。
如果是这种情况,我认为最好的解决方案是从每个WAR文件中删除jar文件并将其部署到服务器上的共享类路径文件夹中?
答案 0 :(得分:33)
Java类加载器通常通过在固定序列中的一个或多个位置查找类来工作。例如,从命令行运行应用程序时加载应用程序的类加载器首先查看rt.jar
文件(以及bootclasspath中的其他文件),然后查看类路径指定的目录和JAR文件。
webapp类加载原则上类似,但在实践中稍微复杂一些。对于特定的webapp,webapp的类加载器按以下顺序查找类。例如,Tomcat 6按此顺序查找类:
当然,一旦类加载器找到了它正在寻找的类,它就不会再看了。因此,订单中稍后具有相同名称的类将不会被加载。
复杂的是,Web容器为每个webapp都有一个类加载器,这些类加载器委托给管理公共类的其他类加载器。实际上,这意味着某些类只会为整个容器加载一次(例如1.和2.),而其他类可能会被不同的类加载器多次加载。
(当一个类被多次加载时,它会产生不同的Class
个对象和不同的类静态。就JVM而言,类的版本是不同的类型,你不能从一个版本中进行类型转换到另一个。)
最后,Tomcat可以配置为允许单个webapps“热负载”。这需要停止webapp,为它创建一个新的类加载器,然后重新启动它。
<强>后续强>
所以...同步静态方法不会保护对多次加载类的共享资源的访问?
这取决于细节,但可能不会。 (或者,如果一个类已经多次加载了实际,那么另外一种方式,那么该类的每个“加载”的static
方法将访问另一组{{ 1}}字段。)
如果您确实希望单个应用程序类实例由同一容器中的多个Web应用程序共享,则最简单的方法是将类放入static
或等效类。但你也应该问问自己这是不是很好的系统设计。考虑组合webapps,或使用请求转发等而不是共享数据结构。单例模式往往在webapps中很麻烦,而且这种风格更是如此。
答案 1 :(得分:4)
Java EE应用程序服务器通常使用多个类加载器来隔离应用程序,并允许部署一个应用程序的新版本,而不会影响其他应用程序。
您可以在具有类加载器层次结构的EAR中获得多个WAR文件和一个EJB文件等模式,每个WAR都有自己的模块。
这会导致你所描述的重复,但这并不是一件坏事。这意味着您甚至可以同时部署相同JAR的不同版本,这实际上可能是有益的,允许增量迁移到新版本。
某些应用程序服务器(WebSphere for exmaple)明确支持共享库概念,我确实使用它。
要小心将JAR弹出到任意类路径中,您就有可能破坏应用服务器本身的稳定性。
答案 2 :(得分:0)
大多数应用程序服务器使用沿路径最具体的策略。 如果你有多个库做同样的事情,你应该考虑将它们放在应用服务器lib中(f.e:TOMCAT_HOME / lib)