• 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 方法重新执行一次。

    二、源码分析

    未完成,待续...

  • 相关阅读:
    红黑树
    jsp简单练习-简单的下拉表单
    【源代码】TreeMap源代码剖析
    ScrollView垂直滚动控件
    进度条控件基本使用
    时间对话框的使用
    DatePicker日期与时间控件
    ImageView显示网络上的图片
    bitmap==null
    android.os.NetworkOnMainThreadException
  • 原文地址:https://www.cnblogs.com/caoxb/p/12526736.html
Copyright © 2020-2023  润新知