Tomcat源码解析系列二:Tomcat类加载机制以及Context的初始化

Tomcat类加载机制以及Context的初始化

概述

本节简单介绍Java虚拟机规范中提到的主要类加载器:

  • Bootstrap Loader:加载lib目录下或者System.getProperty(“sun.boot.class.path”)、或者-XBootclasspath所指定的路径或jar
  • Extended Loader:加载lib\ext目录下或者System.getProperty(“java.ext.dirs”) 所指定的 路径或jar。例如:java -Djava.ext.dirs=/projects/testproj/classes HelloWorld
  • AppClassLoader:加载-cp或者System.getProperty(“java.class.path”)所指定的 路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp /projects/testproj/classes HelloWorld
  • Tomcat的commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;加载" c a t a l i n a . b a s e / l i b " , " {catalina.base}/lib"," catalina.base/lib","{catalina.base}/lib/.jar"," c a t a l i n a . h o m e / l i b " , " {catalina.home}/lib"," catalina.home/lib","{catalina.home}/lib/.jar"里面的类
  • Tomcat的WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见

tomcat使用的自定义类加载器

tomcat使用了两个自己定义的类加载器,其模型如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f283HSmg-1684648970844)(assets/image-20220724095224325.png)]

源码分析

commonClassLoader初始化

在Bootstrap.init()中调Bootstrap#initClassLoaders进行初始化

private void initClassLoaders() {
   
  try {
   
    // commonLoader的加载路径为common.loader(properties文件配置)
    // 加载${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar里面的类
    commonLoader = createClassLoader("common", null);
    if( commonLoader == null ) {
   
      // no config file, default to this loader - we might be in a 'single' env.
      commonLoader=this.getClass().getClassLoader();
    }
    // 加载路径为server.loader,默认为空,父类加载器为commonLoader
    catalinaLoader = createClassLoader("server", commonLoader);
    // 加载路径为shared.loader,默认为空,父类加载器为commonLoader
    sharedLoader = createClassLoader("shared", commonLoader);
  } catch (Throwable t) {
   
    handleThrowable(t);
    log.error("Class loader creation threw exception", t);
    System.exit(1);
  }
}

Context的初始化包含ParallerWebAppClassLoader的初始化

加载的类存放的位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YuzlnsL6-1684648970846)(assets/image-20220724153842621.png)]

其中:

allResources[1]=mainResources;// /WEB-INF/classes包含的类
allResources[1]=classResources;// /WEB-INF/lib包含的jar中的类

在StandardContext#startInternal()->实例化WebappLoader->WebappLoader.start()->实例化ParallerWebAppClassLoader

先看StandardContext.startInternal()的逻辑

protected synchronized void startInternal() throws LifecycleException {
   

    if(log.isDebugEnabled())
        log.debug("Starting " + getBaseName());

    // Send j2ee.state.starting notification
    if (this.getObjectName() != null) {
   
        Notification notification = new Notification("j2ee.state.starting",
                this.getObjectName(), sequenceNumber.getAndIncrement());
        broadcaster.sendNotification(notification);
    }

    setConfigured(false);
    boolean ok = true;

    // Currently this is effectively a NO-OP but needs to be called to
    // ensure the NamingResources follows the correct lifecycle
    if (namingResources != null) {
   
        namingResources.start();
    }

    // Post work directory
    postWorkDirectory();

    // Add missing components as necessary
    if (getResources() == null) {
      // (1) Required by Loader
        if (log.isDebugEnabled())
            log.debug("Configuring default Resources");

        try {
   
            // 设置 context 的 Resources 为 StandardRoot
            setResources(new StandardRoot(this));
        } catch (IllegalArgumentException e) {
   
            log.error(sm.getString("standardContext.resourcesInit"), e);
            ok = false;
        }
    }
    if (ok) {
   
        // StandardRoot.startInternal() 初始化要加载allResources包含
        // 重要:  1.mainResources: /WEB-INF/classes包含的jar中的类
        // 重要:  2.classResources: /WEB-INF/lib包含的jar中的类
        // 3.preResources: 是context.xml中定义的 preResources 资源
        // 4.postResources: 是context.xml中定义的 postResources资源
        // 5.classResources
        resourcesStart();
    }

    if (getLoader() == null) {
   
        // WebappLoader.loaderClass=ParallelWebappClassLoader.class.getName()标识context应用使用的应用类加载器
        WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
        webappLoader.setDelegate(getDelegate());
        setLoader(webappLoader);
    }

    // An explicit cookie processor hasn't been specified; use the default
    if (cookieProcessor == null) {
   
        cookieProcessor = new Rfc6265CookieProcessor();
    }

    // Initialize character set mapper
    getCharsetMapper();

    // Validate required extensions
    boolean dependencyCheck = true;
    try {
   
        dependencyCheck = ExtensionValidator.validateApplication
            (getResources(), this);
    } catch (IOException ioe) {
   
        log.error(sm.getString("standardContext.extensionValidationError"), ioe);
        dependencyCheck = false;
    }

    if (!dependencyCheck) {
   
        // do not make application available if dependency check fails
        ok = false;
    }

    // Reading the "catalina.useNaming" environment variable
    String useNamingProperty = System.getProperty("catalina.useNaming");
    if ((useNamingProperty != null)
        && (useNamingProperty.equals("false"))) {
   
        useNaming = false;
    }

    if (ok && isUseNaming()) {
   
        if (getNamingContextListener() == null) {
   
            NamingContextListener ncl = new NamingContextListener();
            ncl.setName(getNamingContextName());
            ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
            addLifecycleListener(ncl);
            setNamingContextListener(ncl);
        }
    }

    // Standard container startup
    if (log.isDebugEnabled())
        log.debug("Processing standard container startup");


    // Binding thread
    ClassLoader oldCCL = bindThread();

    try {
   
        if (ok) {
   
            // Start our subordinate components, if any
            Loader loader = getLoader();
            if (loader instanceof Lifecycle) {
   
                // 在WebappLoader.startInternal中实例化类加载器为:ParallelWebappClassLoader
                // 设置在WebappLoader.classLoader=ParallelWebappClassLoader之后创建EventListener,Filter,Servlet
                ((Lifecycle) loader).start();
            }

            // since the loader just started, the webapp classloader is now
            // created.
            setClassLoaderProperty("clearReferencesRmiTargets",
                    getClearReferencesRmiTargets());
            setClassLoaderProperty("clearReferencesStopThreads",
                    getClearReferencesStopThreads());
            setClassLoaderProperty("clearReferencesStopTimerThreads",
                    getClearReferencesStopTimerThreads());
            setClassLoaderProperty("clearReferencesHttpClientKeepAliveThread",
                    getClearReferencesHttpClientKeepAliveThread());
            setClassLoaderProperty("clearReferencesObjectStreamClassCaches",
                    getClearReferencesObjectStreamClassCaches());
            setClassLoaderProperty("clearReferencesThreadLocals",
                    getClearReferencesThreadLocals());

            // By calling unbindThread and bindThread in a row, we setup the
            // current Thread CCL to be the webapp classloader
            unbindThread(oldCCL);
            oldCCL = bindThread();

            // Initialize logger again. Other components might have used it
            // too early, so it should be reset.
            logger = null;
            getLogger();

            Realm realm = getRealmInternal();
            if(null != realm) {
   
                if (realm instanceof Lifecycle) {
   
                    ((Lifecycle) realm).start();
                }

                // Place the CredentialHandler into the ServletContext so
                // applications can have access to it. Wrap it in a "safe"
                // handler so application's can't modify it.
                CredentialHandler safeHandler = new CredentialHandler() {
   
                    @Override
                    public boolean matches(String inputCredentials, String storedCredentials) {
   
                        return getRealmInternal().getCredentialHandler().matches(inputCredentials, storedCredentials);
                    }

                    @Override
                    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值