Tomcat热加载原理剖析

一、热加载时序图

在这里插入图片描述

二、热加载原理剖析

开启热加载在<Context标签中配置reloadable="true"

1、StandardEngine容器启动startInternal(),内部会调用父类ContainerBase的startInternal()

    protected synchronized void startInternal() throws LifecycleException {

        // Log our server identification information
        if(log.isInfoEnabled())
            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());

        // Standard container startup
        // 调用ContainerBase的startInternal,以异步的方式启动子容器
        super.startInternal();
    }

2、进入ContainerBase startInternal(),注意方法最后threadStart()中会开启一个后台线程

 protected synchronized void startInternal() throws LifecycleException {

        // Start our subordinate components, if any
        // 启动下级组件,如果有的话

        // 容器的类加载器
        Loader loader = getLoaderInternal();
        if ((loader != null) && (loader instanceof Lifecycle))
            ((Lifecycle) loader).start();

       /*此处移除部分代码
       */
        //开启管道、也是层层调用开启
        if (pipeline instanceof Lifecycle) {
            ((Lifecycle) pipeline).start();
        }

        // 这个时候会触发START_EVENT事件,会进行deployApps
        setState(LifecycleState.STARTING);
        // Start our thread
        // Engine容器启动一个background线程
        threadStart();
    }

3、进入threadStart()

    protected void threadStart() {

        if (thread != null)
            return;

//        System.out.println(this.getInfo() + "的backgroundProcessorDelay等于=" + backgroundProcessorDelay);
        // 默认情况下只有Engine的backgroundProcessorDelay大于0,为10,
        // 也就是说,虽然每个容器在启动的时候都会走到当前方法,但是只有Engine能继续往下面去执行
        // 但是其他容器是可以配置backgroundProcessorDelay属性的,只要配置了大于0,那么这个容器也会单独开启一个backgroundProcessor线程

        if (backgroundProcessorDelay <= 0)
            return;

        threadDone = false;
        String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
        // ContainerBackgroundProcessor线程每隔一段时间会调用容器内的backgroundProcess方法,并且会调用子容器的backgroundProcess方法
        thread = new Thread(new ContainerBackgroundProcessor(), threadName);
        thread.setDaemon(true);
        thread.start();

    }

4、进入后台ContainerBackgroundProcessor线程中
threadDone为false,这个后台线程一直会运行,主要作用遍历StandardEngine容器中子容器并调用其backgroundProcess()

        public void run() {
            Throwable t = null;
            String unexpectedDeathMessage = sm.getString(
                    "containerBase.backgroundProcess.unexpectedThreadDeath",
                    Thread.currentThread().getName());
            try {
                while (!threadDone) {
                    try {
                        Thread.sleep(backgroundProcessorDelay * 1000L);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                    if (!threadDone) {
                        // 获取当前的容器
                        Container parent = (Container) getMappingObject();
                        ClassLoader cl =
                            Thread.currentThread().getContextClassLoader();
//                        System.out.println("ContainerBackgroundProcessor在运行"+ parent.getName());
                        if (parent.getLoader() != null) {
                            System.out.println(parent.getName() + "有loader");
                            cl = parent.getLoader().getClassLoader();
                        }
                        // 执行子容器的background
                        processChildren(parent, cl);
                    }
                }

5、进入processChildren(),此方法是父类ContainerBase的即每个容器都会走,每个容器都会调用backgroundProcess(),大致流程engine、host、context、wrapper,总之最终会走到context的backgroundProcess

        protected void processChildren(Container container, ClassLoader cl) {
            try {
                if (container.getLoader() != null) {
                    Thread.currentThread().setContextClassLoader
                        (container.getLoader().getClassLoader());
                }
                //
                container.backgroundProcess();
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error("Exception invoking periodic operation: ", t);
            } finally {
                Thread.currentThread().setContextClassLoader(cl);
            }
            Container[] children = container.findChildren();
            for (int i = 0; i < children.length; i++) {
                if (children[i].getBackgroundProcessorDelay() <= 0) {
                    // 调用子容器的backgroundProcess方法, delay小于0才调用,如果大于0,则该容器会有自己单独的background线程
                    processChildren(children[i], cl);
                }
            }
        }
    }

6、进入StandardContext的backgroundProcess(),此方法还是会调用父类ContainerBase的backgroundProcess()

public void backgroundProcess() {
		//省去多余代码,获取context中webapploader
        // 热加载
             // 热加载
        Loader loader = getLoaderInternal();  // Context.webapploader
        if (loader != null) {
            try {
                loader.backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e);
            }
        }
    }

7、进入webapploader的backgroundProcess(),可以看到会重新加载Context

    public void backgroundProcess() {
        if (reloadable && modified()) {
            System.out.println(container.getInfo()+"触发了热加载");
            try {
                Thread.currentThread().setContextClassLoader
                    (WebappLoader.class.getClassLoader());
                if (container instanceof StandardContext) {
                	//重新reload
                    ((StandardContext) container).reload();
                }
            } finally {
                if (container.getLoader() != null) {
                    Thread.currentThread().setContextClassLoader
                        (container.getLoader().getClassLoader());
                }
            }
        } else {
            closeJARs(false);
        }
    }
8、进入StandardContext的reload()、其实主要做了如下四步
public synchronized void reload() {


        // Stop accepting requests temporarily.
        setPaused(true);//
       
            stop();

   
            start();


        setPaused(false);



    }

9、进入Context的start()中也就进入类似重新开启context,重新加载context中的类,最终会调用WebAppClassLoader的loadClass()
主要几个步骤:
1、先去tomcat自己维护的map中去找有没有加载
2、再去jvm中找有没有加载
3、先尝试通过系统类加载器去加载(主要是加载jdk核心类)
4、然后根据delegateLoad,是否委托父类去加载,如果没有委托,自己先去加载、加载不到再让父类加载。如果委托,先让父类加载器加载、自己再加载,就是先后顺序

  public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {

        synchronized (getClassLoadingLockInternal(name)) {
            if (log.isDebugEnabled())
                log.debug("loadClass(" + name + ", " + resolve + ")");
            Class<?> clazz = null;

            // Log access to stopped classloader
            if (!started) {
                try {
                    throw new IllegalStateException();
                } catch (IllegalStateException e) {
                    log.info(sm.getString("webappClassLoader.stopped", name), e);
                }
            }

            // (0) Check our previously loaded local class cache
            // 先检查该类是否已经被Webapp类加载器加载。
            clazz = findLoadedClass0(name); // map
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }

            // (0.1) Check our previously loaded class cache
            // 该方法直接调用findLoadedClass0本地方法,findLoadedClass0方法会检查JVM缓存中是否加载过此类
            clazz = findLoadedClass(name);   // jvm 内存
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }

            // (0.2) Try loading the class with the system class loader, to prevent
            //       the webapp from overriding J2SE classes
            // 尝试通过系统类加载器(AppClassLoader)加载类,防止webapp重写JDK中的类
            // 假设,webapp想自己去加载一个java.lang.String的类,这是不允许的,必须在这里进行预防。
            try {
                clazz = j2seClassLoader.loadClass(name);    // java.lang.Object
                if (clazz != null) {
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }

            // (0.5) Permission to access this class when using a SecurityManager
            if (securityManager != null) {
                int i = name.lastIndexOf('.');
                if (i >= 0) {
                    try {
                        securityManager.checkPackageAccess(name.substring(0,i));
                    } catch (SecurityException se) {
                        String error = "Security Violation, attempt to use " +
                            "Restricted Class: " + name;
                        if (name.endsWith("BeanInfo")) {
                            // BZ 57906: suppress logging for calls from
                            // java.beans.Introspector.findExplicitBeanInfo()
                            log.debug(error, se);
                        } else {
                            log.info(error, se);
                        }
                        throw new ClassNotFoundException(error, se);
                    }
                }
            }

            boolean delegateLoad = delegate || filter(name); // 委托--true

            // (1) Delegate to our parent if requested
            // 是否委派给父类去加载
            if (delegateLoad) {
                if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader1 " + parent);
                try {
                    clazz = Class.forName(name, false, parent);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return (clazz);
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }

            // (2) Search local repositories
            // 从webapp应用内部进行加载
            if (log.isDebugEnabled())
                log.debug("  Searching local repositories");
            try {
                clazz = findClass(name);  // classes,lib
                if (clazz != null) {
                    if (log.isDebugEnabled())
                        log.debug("  Loading class from local repository");
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }

            // (3) Delegate to parent unconditionally
            // 如果webapp应用内部没有加载到类,那么无条件委托给父类进行加载
            if (!delegateLoad) {
                if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader at end: " + parent);
                try {
                    clazz = Class.forName(name, false, parent);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return (clazz);
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }
        }

        throw new ClassNotFoundException(name);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值