tomcat 类加载器


tomcat 类加载器

                 

官网: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部署的应用可能会报错

              

              

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值