赞
踩
官网:Apache Tomcat 10 (10.0.14) - Class Loader How-To
java 类加载器
双亲委派模型(java8及之前类加载模型)
类加载器说明
- # 启动类加载器
- 加载JAVA_HOME/lib目录、参数-Xbootclasspath指定路径下的类
- 文件名需要能被虚拟机识别(如rt.jar、tools.jar),如果不能识别,即使在路径下也不会被加载
-
- # 拓展类加载器
- 加载JAVA_HOME/lib/ext目录、java.ext.dirs系统变量指定目录下的类
- 可将具有通用性的类库放置在ext目录里来java se的功能
-
- # 应用程序类加载器
- 加载用户类路径(classpath)上的所有类,可以在代码中直接使用这个类加载器
- 如果用户没有自定义类加载器,默认使用该类加载器加载用户自定的类
双亲委派类加载流程
- 类加载器接收到类加载请求,如果当前类加载器加载过类,则直接返回;
- 否则,将请求往上抛,直到启动类加载器,由启动类加载器加载;
- 如果加载不了,则将类加载请求往下抛,由下层类加载器加载类,直到加载到类为止;
- 如果请求抛至直到最初接收到类加载请求的类加载器,还是加载不了类,抛出ClassNotFoundException异常
ClassLoader
- public abstract class ClassLoader {
-
- ...
-
- protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
- {
- synchronized (getClassLoadingLock(name)) {
-
- // First, check if the class has already been loaded
- //如果加载过,从缓存中直接获取类
- Class<?> c = findLoadedClass(name);
-
- if (c == null) { //如果缓存中没有
- long t0 = System.nanoTime();
- try {
- if (parent != null) { //如果存在父类加载器
- c = parent.loadClass(name, false); //父类加载器加载类
- } else {
- c = findBootstrapClassOrNull(name); //不存在,说明当前类加载器为启动类加载器,
- //由启动类加载器加载类
- }
- } catch (ClassNotFoundException e) {
- // ClassNotFoundException thrown if class not found
- // from the non-null parent class loader
- }
-
- if (c == null) { //如果加载不到
- // If still not found, then invoke findClass in order
- // to find the class.
- long t1 = System.nanoTime();
- c = findClass(name); //当前类加载器加载类,找不到则抛出异常
-
- // this is the defining class loader; record the stats
- sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
- sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
- sun.misc.PerfCounter.getFindClasses().increment();
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
- }
-
- ...
- }
破坏双亲委派模型
- 双亲委派模型在java2引入,为向前兼容,提供了findClass类加载类;
- 引入线程上下文类加载器,父类加载器委托子类加载器加载类;
- 模块化热部署:每个模块都有一个类加载器,实现程序热替换;
- java9引入模块化系统,拓展类加载器被平台类加载器替代,加载流程也做出调整
java9 类加载器
平台类加载器、应用程序类加载器在接收到类加载请求时,如果可以判断类所属的模块,优先将类加载请求抛给对应的类加载器
BuiltInClassLoader:启动类加载器、平台类加载器、应用程序类加载器均继承该类
- public class BuiltinClassLoader extends SecureClassLoader
-
- ...
-
- @Override
- protected Class<?> loadClass(String cn, boolean resolve)
- throws ClassNotFoundException
- {
- Class<?> c = loadClassOrNull(cn, resolve);
- if (c == null) //如果最终没加载到类,抛出ClassNotFoundException异常
- throw new ClassNotFoundException(cn);
- return c;
- }
-
- /**
- * A variation of {@code loadClass} to load a class with the specified
- * binary name. This method returns {@code null} when the class is not
- * found.
- */
- protected Class<?> loadClassOrNull(String cn, boolean resolve) {
- synchronized (getClassLoadingLock(cn)) {
- // check if already loaded
- Class<?> c = findLoadedClass(cn); //如果已经加载过该类,直接返回
-
- if (c == null) {
-
- // find the candidate module for this class
- LoadedModule loadedModule = findLoadedModule(cn);
- //查找类所属的模块
- if (loadedModule != null) { //找到类所属模块,直接用对应的类加载器加载
- // package is in a module
- BuiltinClassLoader loader = loadedModule.loader();
- if (loader == this) { //是当前类加载器,则用当前类加载器加载
- if (VM.isModuleSystemInited()) {
- c = findClassInModuleOrNull(loadedModule, cn);
- }
- } else {
- // delegate to the other loader
- c = loader.loadClassOrNull(cn); //如果不是,抛给对应的类加载器
- }
-
- } else {
-
- // check parent
- if (parent != null) { //如果没找到对应的模块,使用父类加载器加载
- c = parent.loadClassOrNull(cn);
- }
-
- // check class path
- if (c == null && hasClassPath() && VM.isModuleSystemInited()) {
- c = findClassOnClassPathOrNull(cn);
- } //如果没加载到类,当前类加载器在类路径中加载
- }
-
- }
-
- if (resolve && c != null)
- resolveClass(c);
-
- return c;
- }
- }
-
- ...
- }
tomcat 类加载器
类加载器默认配置:catalina.properties
- common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
-
- server.loader=
- shared.loader=
Bootstrap:启动类加载器
- This class loader contains the basic runtime classes provided by the Java Virtual
- Machine, plus any classes from JAR files present in the System Extensions directory
- ($JAVA_HOME/jre/lib/ext)
- bootstrap类加载器加载jvm基础类、$JAVA_HOME/jre/lib/ext目录下的拓展类
System:系统类加载器
- This class loader is normally initialized from the contents of the CLASSPATH
- environment variable. All such classes are visible to both Tomcat internal classes,
- and to web applications.
- # 该类加载器通常在CLASSPATH路径下初始化,加载的类对tomcat内部类、应用程序可见
-
- However, the standard Tomcat startup scripts ($CATALINA_HOME/bin/catalina.sh or
- %CATALINA_HOME%\bin\catalina.bat) totally ignore the contents of the CLASSPATH
- environment variable itself, and instead build the System class loader from the
- following repositories:
- $CATALINA_HOME/bin/bootstrap.jar、
- $CATALINA_BASE/bin/tomcat-juli.jar or $CATALINA_HOME/bin/tomcat-juli.jar
- $CATALINA_HOME/bin/commons-daemon.jar
- # 标准启动方式(sh脚本启动、bat脚本启动)会忽略classpath下的内容,
- # 从$CATALINA_HOME/bin目录下的几个jar包创建系统类加载器
common:common类加载器
- This class loader contains additional classes that are made visible to both Tomcat
- internal classes and to all web applications
- # common类加载器加载的类对tomcat内部类、应用程序均可见
-
- Normally, application classes should NOT be placed here. The locations searched by
- this class loader are defined by the common.loader property in $CATALINA_BASE/conf
- /catalina.properties. The default setting will search the following locations in
- the order they are listed:
- unpacked classes and resources in $CATALINA_BASE/lib
- JAR files in $CATALINA_BASE/lib
- unpacked classes and resources in $CATALINA_HOME/lib
- JAR files in $CATALINA_HOME/lib
- #通常应用程序的类不应该放置在common类加载器的加载目录
- #common类加载器加载目录在catalina.properties中设置,
- #默认加载顺序:$CATALINA_BASE/lib解压的类、$CATALINA_BASE/lib中的jar包
- $CATALINA_HOME/lib解压的类、$CATALINA_HOME/lib中的jar包
webappX:web应用类加载器
- A class loader is created for each web application that is deployed in a single
- Tomcat instance. All unpacked classes and resources in the /WEB-INF/classes
- directory of your web application, plus classes and resources in JAR files under
- the /WEB-INF/lib directory of your web application, are made visible to this web
- application, but not to other ones.
- # web应用类加载器由应用程序创建
- # web应用类加载器会加载/WEB-INF/classes下的解压类、/WEB-INF/lib下的jar包
- # 加载的类只对本应用可见,对其他应用不可见
web应用类加载器委派规则
- As mentioned above, the web application class loader diverges from the default Java
- delegation model (in accordance with the recommendations in the Servlet
- Specification, version 2.4, section 9.7.2 Web Application Classloader).
- #web应用类加载器打破java双亲委派模型规则
-
- When a request to load a class from the web application's WebappX class loader is
- processed, this class loader will look in the local repositories first, instead of
- delegating before looking. There are exceptions. Classes which are part of the JRE
- base classes cannot be overridden. There are some exceptions such as the XML parser
- components which can be overridden using the appropriate JVM feature which is the
- endorsed standards override feature for Java <= 8 and the upgradeable modules
- feature for Java 9+. Lastly, the web application class loader will always delegate
- first for Jakarta EE API classes for the specifications implemented by Tomcat
- (Servlet, JSP, EL, WebSocket).
- #web应用类加载器收到类加载请求时,首先在本地目录中查找类,不会进行委派(java基础类除外)
- #特殊情况:java基础类不会被覆盖(基础类加载请求始终会委派给bootClassLoader加载)、
- xml解析工具覆盖措施:java8(含)以下使用enndorsed机制覆盖、java9以上使用可升级模块特性覆盖
- Jakarta EE API classes类总是会进行委派,不会在本地进行加载
- All other class loaders in Tomcat follow the usual delegation pattern
- #其他的类加载器都遵循双亲委派规则
从web应用类加载器角度看,类、资源文件加载顺序如下
- #默认委派规则
- Bootstrap classes of your JVM
- /WEB-INF/classes of your web application
- /WEB-INF/lib/*.jar of your web application
- System class loader classes (described above)
- Common class loader classes (described above)
-
- #强制使用双亲委派规则:<Loader delegate="true"/> (server.xml中配置)
- Bootstrap classes of your JVM
- System class loader classes (described above)
- Common class loader classes (described above)
- /WEB-INF/classes of your web application
- /WEB-INF/lib/*.jar of your web application
xml 解析工具覆盖
- Starting with Java 1.4 a copy of JAXP APIs and an XML parser are packed inside the
- JRE. This has impacts on applications that wish to use their own XML parser.
- #从java4开始,JAXP APIs、XML parser内置在jre里面,用户使用自定义的解析工具的方式需要变更
-
- In old versions of Tomcat, you could simply replace the XML parser in the Tomcat
- libraries directory to change the parser used by all web applications. However,
- this technique will not be effective when you are running modern versions of Java,
- because the usual class loader delegation process will always choose the
- implementation inside the JDK in preference to this one.
- #在老的版本里面,用户可直接替换tomcat目录里面的xml解析工具
- #java4解析工具内置到jre里面后,这种直接替换的方式方式不起作用
-
- Java <= 8 supports a mechanism called the "Endorsed Standards Override Mechanism"
- to allow replacement of APIs created outside of the JCP (i.e. DOM and SAX from
- W3C). It can also be used to update the XML parser implementation. For more
- information, see: http://docs.oracle.com/javase/1.5.0/docs/guide/standards
- /index.html. For Java 9+, use the upgradeable modules feature.
- #java8(含)以下提供endorsed机制进行替换
- #java9以上使用可升级的模块特性进行替换
-
- Tomcat utilizes the endorsed mechanism by including the system property setting
- -Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS in the command line that starts the
- container. The default value of this option is $CATALINA_HOME/endorsed. This
- endorsed directory is not created by default. Note that the endorsed feature is no
- longer supported with Java 9 and the above system property will only be set if
- either the directory $CATALINA_HOME/endorsed exists, or the variable
- JAVA_ENDORSED_DIRS has been set.
- #endorsed使用:-Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS
- #tomcat endorsed默认目录:$CATALINA_HOME/endorsed,这个目录默认不会自动创建
- #java9及以上不支持endorsed机制
-
- Note that overriding any JRE component carries risk. If the overriding component
- does not provide a 100% compatible API (e.g. the API provided by Xerces is not 100%
- compatible with the XML API provided by the JRE) then there is a risk that Tomcat
- and/or the deployed application will experience errors.
- #覆盖jre组件会有风险,如果提供的api不能100%和jre提供的xml api适配,
- #tomcat服务器、tomcat部署的应用可能会报错
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。