ClassLoader熱加載的簡單實現
阿新 • • 發佈:2018-07-18
錯誤 .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) throwsClassNotFoundException { 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熱加載的簡單實現