場景:java
本地調試(頻繁的啓動/中止服務器)web
線上發佈(每次都須要啓動/中止服務器)tomcat
優勢:服務器
不管本地仍是線上,都適用app
無需重啓服務器,提升開發、調試效率;提高發布、運維效率,下降運維成本運維
1、java實現熱部署有哪幾種方式?webapp
經過配置tomcat,直接把項目放在webapps裏。jvm
在tomcat\conf\server.xml中的<host></host>中添加<context debug="0" docBase="" path="" privileged="true" reloadable="true"/>標籤ide
在tomcat\conf\Catalina\localhost中添加xml,關鍵屬性<context docBase="" reloadable="true" />測試
實現一個Java類熱加載的實例
1.定義類加載器
//自定義類加載器,需實現findClass方法(核心類) public class MyClassLoader extends ClassLoader { //定義加載的路徑 private String classPath; public MyClassLoader(String classPath) { //調用父類的加載器 super(ClassLoader.getSystemClassLoader()); this.classPath = classPath; } //1.從新findClass方法 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] b = this.loadData(name); return this.defineClass(name, b, 0, b.length); } //加載class文件中的內容 private byte[] loadData(String name) { //.替換爲//表示 name = name.replaceAll(".", "//"); try { FileInputStream is = new FileInputStream(new File(classPath + name + ".class")); ByteArrayOutputStream aos = new ByteArrayOutputStream(); int b = 0; while((b = is.read())!=-1){ aos.write(b); } is.close(); aos.close(); return aos.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }
2.實現熱加載的功能(即:哪些方法被熱加載了,能夠動態更新)
//該子類下的須要動態更新 public interface BaseManager { public void logic(); }
//實現具體的熱加載功能 public class MyManager implements BaseManager{ @Override public void logic() { System.out.println("熱加載:logic..."); } }
3.定義封裝類,定義須要加載的信息
public class LoadInfo { private MyClassLoader loader; //(起一個線程)記錄要加載的類的時間戳-->加載的時間 private long loadTime; private BaseManager manager; public LoadInfo(MyClassLoader loader, long loadTime) { super(); this.loader = loader; this.loadTime = loadTime; } //get、set方法 }
4.創建工廠類,加載manager
//加載manager的工廠 public class ManagerFactory { //記錄熱加載類的加載信息 private static final Map<String, LoadInfo> loadTimeMap = new HashMap<>(); //要加載類的classpath private static final String CLASS_PATH = "E:/workspace/class_loader/bin"; //實現熱加載的類的全名稱(包名+類名) public static final String MY_MANAGER = "class_loader.MyManager"; public static BaseManager getManager(String className){ File loadFile = new File(CLASS_PATH+className.replaceAll("\\.", "/")+".class"); //取的最後一次的修改時間 long lastModified = loadFile.lastModified(); //loadTimeMap不包含className爲key的LoadInfo信息,證實這個類沒有被加載,那麼須要加載這個類到jvm if (loadTimeMap.get(className) == null) { load(className,lastModified); //加載類的時間戳變化了,一樣須要從新加載這個類到jvm }else if (loadTimeMap.get(className).getLoadTime() != lastModified) { load(className,lastModified); } return loadTimeMap.get(className).getManager(); } private static void load(String className, long lastModified) { MyClassLoader myClassLoader = new MyClassLoader(CLASS_PATH); Class<?> loadClass = null; try { loadClass = myClassLoader.loadClass(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } BaseManager manager = newInstance(loadClass); LoadInfo loadInfo = new LoadInfo(myClassLoader, lastModified); loadInfo.setManager(manager); loadTimeMap.put(className, loadInfo); } //以反射的方式建立BaseManager子類對象 private static BaseManager newInstance(Class<?> loadClass) { try { return (BaseManager) loadClass.getConstructor(new Class[]{}).newInstance(new Object[]{}); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { e.printStackTrace(); } return null; } }
5.檢測修改過的class
//後臺啓動一個線程不斷刷新從新加載實現熱加載的類 public class MsgHandler implements Runnable{ @Override public void run() { while(true){ BaseManager manager = ManagerFactory.getManager(ManagerFactory.MY_MANAGER); manager.logic(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
6.測試
public class ClassLoaderTest { public static void main(String[] args) { new Thread(new MsgHandler()).start(); } }
運行Test,結果以下:
2、Spring Boot怎麼實現熱部署?
實現方式有兩種
使用Spring Loaded
使用Spring-boot-devtools
第一種:
第二種: