zoukankan      html  css  js  c++  java
  • Tomcat源码分析--热部署原理

    热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。tomcat支持当你对这个文件进行修改时,会重新把这个新的文件加载到JVM中。当然这个功能是需要我们进行配置的。

    我们可以在server.xml 中的 Host标签下配置一个Context标签,这里的reloadable="true",就表示是否重新加载,即我们所说的热部署。

    <Context path="jsp-web" reloadable="true" docBase="D:workspace-nenojsp-webWebContent">
        <!-- DevLoader 加载指定位置的jar包及编译后的classes文件夹所有内容 -->
        <Loader className="org.apache.catalina.loader.DevLoader" reloadable="true" useSystemClassLoaderAsParent="false" />
    </Context>    

    先不说其实现原理,先来一段示例:

     一、示例

    1.新建一个项目名称为demo1

    添加一个测试类和一个依赖jar

    public class PrintTest {
    
        /**
         * 打印测试
         */
        public void print() {
            List<String> list = new ArrayList<String>();
            // 第三方依赖jar,如果jar包没有加载进来,或者是 com.alibaba.fastjson.JSONArray 没有加载会抛出异常
            JSONArray arr = new JSONArray();
            // 用来打印测试输出的
            System.out.println("111fdasfd33311");
        }
    }

    2.新建一个项目名称为demo2

    (1)自定义一个类加载器

    package com.test;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.net.URLClassLoader;
    /**
     * 自定义类加载器:加载自定义的类文件
     */
    public class MyClassLoader extends ClassLoader {
        /**
         * 这里CLASS_PATH 使用的是demo1项目路径
         */
        static final String CLASS_PATH = "D:\workspace-neno\demo1\bin";
        
        URLClassLoader loader = null;
        
        public MyClassLoader(URLClassLoader loader) {
            this();
            this.loader = loader;
        }
    
        public MyClassLoader() {
            super(ClassLoader.getSystemClassLoader());
        }
    
        @Override
        protected Class<?> findClass(String className) throws ClassNotFoundException {
            File file = new File(CLASS_PATH + "/" + className.replace(".", "/") + ".class");
            if (file.exists()) {
                try {
                    FileInputStream fis = new FileInputStream(file);
                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
                    int b = 0;
                    while ((b = fis.read()) != -1) {
                        bout.write(b);
                    }
                    fis.close();
                    byte[] _byte = bout.toByteArray();
                    return super.defineClass(className, _byte, 0, _byte.length);
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } else {
                // 如果文件不存在,说明是jar包中的类,因此使用 loader 来加载这个类
                return loader.loadClass(className);
            }
            return null;
        }
    
    }

     (2)自定义一个URLClassLoader类

    package com.test;
    
    import java.net.URL;
    import java.net.URLClassLoader;
    /**
     * 继承 URLClassLoader,目录是为了重写一个addURL方法
     *
     */
    public class MyURLClassLoader extends URLClassLoader {
        
        public MyURLClassLoader(URL[] urls) {
            super(urls);
        }
    
        @Override
        protected void addURL(URL url) {
            super.addURL(url);
        }
    }

     (3)添加一个测试类

    package com.test;
    
    import java.io.File;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class LoadJarsAndClassesTest {
    
        public static MyURLClassLoader loader = null;
        // ../classes 或bin目录下所有文件的目录
        public static String CLASSES_PATH = "D:\workspace-neno\demo1\bin";
        // jar文件存放的目录
        public static String JARS_PATH = "D:\workspace-neno\demo1\lib";
    
        public static void main(String[] args)
                throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
                SecurityException, IllegalArgumentException, InvocationTargetException {
            
            LoadJarsAndClassesTest test = new LoadJarsAndClassesTest();
            
            // 加载jar文件        
            test.loadJarsFile();
         // 一直打印输出,测试修改代码时,输出的内容是否发生变化
    while (true) { // 加载类文件 test.loadClassesFile(); test.printTest(); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 1.加载其它项目下自定义的类文件 2.并实例化 3.调用其它项目下自定义类文件下的方法 */ private void printTest() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException { MyClassLoader myClassLoader = new MyClassLoader(loader); Class<?> clazz = myClassLoader.findClass("com.print.PrintTest"); Object object = clazz.newInstance(); Method method = clazz.getDeclaredMethod("print", null); method.invoke(object, null); } /** * 通过 URLClassLoader 加载.class所在的根目录 */ private void loadClassesFile() { File classesDir = new File(CLASSES_PATH); if (classesDir.exists()) { try { URL url = classesDir.toURI().toURL(); loader.addURL(url); } catch (MalformedURLException e) { e.printStackTrace(); } } } /** * 通过 URLClassLoader 加载lib目录下所有的jar文件 */ private void loadJarsFile() { File jarsDir = new File(JARS_PATH); if (jarsDir.exists()) { try { File[] jarFiles = jarsDir.listFiles(); URL[] urls = new URL[jarFiles.length]; int i = 0; for (File file : jarFiles) { URL url = file.toURI().toURL(); urls[i++] = url; } loader = new MyURLClassLoader(urls); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }

     (4)启动测试,修改demo1项目中的PrintTest 中print方法中的输出内容

    总结:

    (1)MyClassLoader 这个类主要是用来加载自定义类.class文件,这里指的就是PrintTest.class文件;

    (2)URLClassLoader 这个类主要是用来加载jar文件 及 bin 或者 classes 下所有的文件包括.class文件及.xml文件等;

    (3)Tomcat 热部署其原理和我的这个示例是一样的。Tomcat启用了一个监听器线程,这个线程其实也是一个循环监听,tomcat每10秒监听一次文件是否修改,如果修改了,则通过重新加载.class文件、xml文件等,就相当于将我这里的loadClassesFile 方法重新执行一次。

    二、源码分析

    未完成,待续...

  • 相关阅读:
    (转载)SAPI 包含sphelper.h编译错误解决方案
    C++11标准的智能指针、野指针、内存泄露的理解(日后还会补充,先浅谈自己的理解)
    504. Base 7(LeetCode)
    242. Valid Anagram(LeetCode)
    169. Majority Element(LeetCode)
    100. Same Tree(LeetCode)
    171. Excel Sheet Column Number(LeetCode)
    168. Excel Sheet Column Title(LeetCode)
    122.Best Time to Buy and Sell Stock II(LeetCode)
    404. Sum of Left Leaves(LeetCode)
  • 原文地址:https://www.cnblogs.com/caoxb/p/12526736.html
Copyright © 2011-2022 走看看