1. 程式人生 > >ClassLoader熱加載的簡單實現

ClassLoader熱加載的簡單實現

錯誤 .class 線程 try 通過反射 obj gets implement static

當我們在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();
    }
}

ClassLoader熱加載的簡單實現