zoukankan      html  css  js  c++  java
  • ClassLoader热加载的简单实现

    当我们在eclipse中修改了一个.java文件时,并通过【ctrl + s 】保存了此java文件,相应的bin目录中,会发现.class文件也发生了修改。通常情况下,java文件是在我们的web项目已经启动了的情况下进行修改的,而.class文件早已加载至虚拟机中。因 此,在没有使用热部署插件的情况下,必须重启tomcat服务。而热部署插件其原理就是将修改后的.class文件重新加载至jvm中的。

    public class Test {
    
    
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
            ClassLoader classLoader = Test.class.getClassLoader();
            Class<?> clazz = classLoader.loadClass("com.classloader.Test");
            Test test = (com.classloader.Test) clazz.newInstance();
            test.logic();
        }
        
        public void logic() {
            System.out.println("hello classloader");   
        }
    }

    1.自定义一个MyClassLoader 类

    public class MyClassLoader extends ClassLoader {
        
        private static final String CLASS_PATH = System.getProperty("java.class.path");    // 编译生成的.class文件的bin目录
        
        public MyClassLoader() {
            super(ClassLoader.getSystemClassLoader());
        }
        
        @Override
        protected Class<?> findClass(String className) throws ClassNotFoundException {
            byte[] b = loadClassFile(className);
            return super.defineClass(className, b, 0, b.length);
        }
    
        private byte[] loadClassFile(String className) {
            File file = new File(CLASS_PATH + "/" + className + ".class");
            try {
                FileInputStream fis = new FileInputStream(file);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int b = 0;
                while ( (b=fis.read())!=-1 ) {
                    baos.write(b);
                }
                fis.close();
                return baos.toByteArray();
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            return null;
        }
        
    }

    2.创建一个工厂类

    public class ObjectFactory {
    
    	private static final String CLASS_PATH = System.getProperty("java.class.path");	// 编译生成的.class文件的bin目录
    	private static Map<String, Object> map = new HashMap<String, Object>();
    	private static long lastModified;	// 最后修改时间
    	
    	private ObjectFactory() {
    		super();
    	}
    	
    	public static Object getInstance(String className) {
    		File loadFile = new File(CLASS_PATH + "/" + className.replace(".", "/") + ".class");	// 打开项目中bin目录下的*.class文件
    		long newModified = loadFile.lastModified();
    		// 文件第一次加载,通过反射的方式创建一个对象
    		if (map.get(className)==null) {
    			loadClass(className);
    		}
    		// .class 文件被修改过,通过ClassLoader方式 创建一个对象
    		if (lastModified!=newModified) {
    			loadClass(className);
    		}
    		lastModified = newModified;
    		return map.get(className);
    	}
    	
    	private static void loadClass(String className) {
    		MyClassLoader myClassLoader = new MyClassLoader();
    		try {
    			Class<?> clazz = myClassLoader.findClass(className);
    			Object object = clazz.newInstance();
    			map.put(className, object);
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		} catch (InstantiationException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IllegalAccessException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IllegalArgumentException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (SecurityException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    }
    

      

    3.定义一个接口,供方法调用【注:这里必须要定义一个接口类,否则会抛出类型转换错误】

    public interface PrintService {
    
        public void print();
    }

    4.接口的实现类

    public class PrintServiceImpl implements PrintService {
    
        @Override
        public void print() {
            System.out.println("测试一下 bbb 1111111111");
        }
    }

    5.编写一个用于观察的线程类

    public class PrintThread implements Runnable {
    
        @Override
        public void run() {
            String className = PrintServiceImpl.class.getName();
            // 一直不断地向控制台输出信息,方便测试“当修改print 中的方法时” 输出信息是否发生变化
            while (true) {
                PrintService printService = (PrintService) ObjectFactory.getInstance(className);
                printService.print();
                
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    public class Test {
    
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
            Thread thread = new Thread(new PrintThread());
            thread.start();
        }
    }
  • 相关阅读:
    多重平行中介(Mplus)
    小米手机,发短信出现闪退
    宇宙是有边还是没边?
    如何查一篇文章的引用文章
    卡方检验
    函数的形参与实参(二维数组)
    输出矩阵四周的数字的平均数(C)
    关于amos 的自由度
    Sql server case when then
    Sql Server中两个表之间数据备份和导入
  • 原文地址:https://www.cnblogs.com/caoxb/p/9326912.html
Copyright © 2011-2022 走看看