Java 类加载器通常通过以固定顺序在一个或多个位置查找类来工作。例如,当您从命令行运行应用程序时加载应用程序的类加载器首先在rt.jar
文件中查找(以及引导类路径上的其他文件),然后在您的类路径指定的目录和 JAR 文件中查找。
webapp 类加载在原理上是类似的,但在实践中要复杂一些。对于特定的 webapp,webapp 的类加载器按以下顺序查找类。例如,Tomcat 6 按以下顺序查找类:
- JVM 的引导类
- 系统类加载器类(描述here)
- /WEB-INF/webapp 的类
- /WEB-INF/lib/*.jar webapp
- $CATALINA_HOME/lib
- $CATALINA_HOME/lib/*.jar
当然,一旦类加载器找到了它正在寻找的类,它就不会再寻找了。因此,顺序中稍后具有相同名称的类将不会被加载。
复杂之处在于,Web 容器为每个 Web 应用程序都有一个类加载器,这些类加载器委托给其他管理公共类的类加载器。实际上,这意味着对于整个容器(例如 1. 和 2.),某些类只会被加载一次,而其他类可能会被不同的类加载器加载多次。
(当一个类被多次加载时,它会导致不同的Class
对象和不同的类静态。就 JVM 而言,类的版本是不同的类型,您不能从一个版本类型转换到另一个版本。)
最后,可以将 Tomcat 配置为允许“热加载”各个 Web 应用程序。这需要停止一个 webapp,为它创建一个新的类加载器,然后重新启动它。
跟进
所以......同步静态方法不会保护对已多次加载类的共享资源的访问?
这取决于细节,但可能不会。(或者换一种方式看,如果一个类实际上被加载了多次,那么static
类的每个“加载”的方法将访问一组不同的static
字段。)
如果您确实希望单个应用程序类实例由同一容器中的多个 webapps 共享,则将类放入$CATALINA_HOME/lib
或等效是最简单的。但是你也应该问问自己这是否是好的系统设计。考虑组合 webapps,或者使用请求转发等而不是共享数据结构。单例模式在 webapps 中往往很麻烦,这种风格更是如此。