欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java基于自定義類加載器實(shí)現(xiàn)熱部署過程解析

 更新時(shí)間:2020年03月13日 09:55:13   作者:yuanyb  
這篇文章主要介紹了Java基于自定義類加載器實(shí)現(xiàn)熱部署過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

熱部署:

熱部署就是在不重啟應(yīng)用的情況下,當(dāng)類的定義即字節(jié)碼文件修改后,能夠替換該Class創(chuàng)建的對(duì)象。一般情況下,類的加載都是由系統(tǒng)自帶的類加載器完成,且對(duì)于同一個(gè)全限定名的java類,只能被加載一次,而且無法被卸載??梢允褂米远x的 ClassLoader 替換系統(tǒng)的加載器,創(chuàng)建一個(gè)新的 ClassLoader,再用它加載 Class,得到的 Class 對(duì)象就是新的(因?yàn)椴皇峭粋€(gè)類加載器),再用該 Class 對(duì)象創(chuàng)建一個(gè)實(shí)例,從而實(shí)現(xiàn)動(dòng)態(tài)更新。如:修改 JSP 文件即生效,就是利用自定義的 ClassLoader 實(shí)現(xiàn)的。

還需要?jiǎng)?chuàng)建一個(gè)守護(hù)線程,不斷地檢查class文件是否被修改過,通過判斷文件的上次修改時(shí)間實(shí)現(xiàn)。

演示:

原來的程序:

修改后重新編譯:

代碼:

package Dynamic;
 
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
 
public class ClassLoadStudy {
  public static void main(String[] args) throws Exception {
    HotDeploy hot = new HotDeploy("Dynamic.Task");
    hot.monitor();
    while (true) {
      TimeUnit.SECONDS.sleep(2);
      hot.getTask().run();
    }
  }
}
 
// 熱部署
 
class HotDeploy {
  private static volatile Runnable instance;
  private final String FILE_NAME;
  private final String CLASS_NAME;
 
  public HotDeploy(String name) {
    CLASS_NAME = name; // 類的完全限定名
    name = name.replaceAll("\\.", "/") + ".class";
    FILE_NAME = (getClass().getResource("/") + name).substring(6); // 判斷class文件修改時(shí)間使用,substring(6)去掉開頭的file:/
  }
 
  // 獲取一個(gè)任務(wù)
  public Runnable getTask() {
    if (instance == null) { // 雙重檢查鎖,單例,線程安全
      synchronized (HotDeploy.class) {
        if (instance == null) {
          try {
            instance = createTask();
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    }
    return instance;
  }
 
  // 創(chuàng)建一個(gè)任務(wù),重新加載 class 文件
  private Runnable createTask() {
    try {
      Class clazz = MyClassLoader.getLoader().loadClass(CLASS_NAME);
      if (clazz != null)
        return (Runnable)clazz.newInstance();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
 
 
  // 監(jiān)視器,監(jiān)視class文件是否被修改過,如果是的話,則重新加載
  public void monitor() throws IOException {
    Thread t = new Thread(()->{
      try {
        long lastModified = Files.getLastModifiedTime(Path.of(FILE_NAME)).toMillis();
        while(true) {
          Thread.sleep(500);
          long now = Files.getLastModifiedTime(Path.of(FILE_NAME)).toMillis();
          if(now != lastModified) { // 如果class文件被修改過了
            lastModified = now;
            instance = createTask(); // 重新加載
          }
        }
      } catch (InterruptedException | IOException e) {
        e.printStackTrace();
      }
    });
    t.setDaemon(true); // 守護(hù)線程
    t.start();
  }
}
 
// 自定義的類加載器
class MyClassLoader extends ClassLoader {
  @Override
  public Class<?> findClass(String name) throws ClassNotFoundException {
    try {
      String fileName = "/" + name.replaceAll("\\.", "/") + ".class";
      InputStream is = getClass().getResourceAsStream(fileName);
      byte[] b = is.readAllBytes();
      return defineClass(name, b, 0, b.length);
    } catch (IOException e) {
      throw new ClassNotFoundException(name);
    }
  }
  public static MyClassLoader getLoader() {
    return new MyClassLoader();
  }
}

遇到的坑:

剛開始自定義類加載器時(shí),重寫的是 loadClass(String name) 方法,但不斷地報(bào)錯(cuò),后來明白了,因?yàn)?Task 類實(shí)現(xiàn)了 Java.lang.Runnable 接口,且重寫 loadClass 方法破壞了雙親委派機(jī)制,導(dǎo)致了自定義的類加載器去加載 java.lang.Runnable,但被Java安全機(jī)制禁止了所以會(huì)報(bào)錯(cuò)。defineClass調(diào)用preDefineClass,preDefineClass 會(huì)檢查包名,如果以java開頭,就會(huì)拋出異常,因?yàn)樽層脩糇远x的類加載器來加載Java自帶的類庫(kù)會(huì)引起混亂。

于是又重寫findClass 方法,但還是不行,findClass方法總是得不到執(zhí)行,因?yàn)榫幾g好的類是在 classpath 下的,而自定義的 ClassLoader 的父加載器是 AppClassLoader,由于雙親委派機(jī)制,類就會(huì)被 Application ClassLoader來加載了。因此自定義的 findClass 方法就不會(huì)被執(zhí)行。解決方法是,向構(gòu)造器 ClassLoader(ClassLoader parent) 傳入null,或傳入 getSystemClassLoader().getParent()。

還有就是路徑問題:

  • path不以 / 開頭時(shí),默認(rèn)是從此類所在的包下取資源;path 以 / 開頭時(shí),則是從ClassPath根下獲??;
    • URL getClass.getResource(String path)
    • InputStream getClass().getResourceAsStream(String path)
    • getResource("") 返回當(dāng)前類所在的包的路徑
    • getResource("/") 返回當(dāng)前的 classpath 根據(jù)路徑
  • path 不能以 / 開始,path 是從 classpath 根開始算的, 因?yàn)閏lassloader 不是用戶自定義的類,所以沒有相對(duì)路徑的配置文件可以獲取,所以默認(rèn)都是從哪個(gè)classpath 路徑下讀取,自然就沒有必要以 / 開頭了 。
    • URL Class.getClassLoader().getResource(String path)
    • InputStream Class.getClassLoader().getResourceAsStream(String path)

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • spring入門教程之bean的繼承與自動(dòng)裝配詳解

    spring入門教程之bean的繼承與自動(dòng)裝配詳解

    眾所周知Spring里面的bean就類似是定義的一個(gè)組件,而這個(gè)組件的作用就是實(shí)現(xiàn)某個(gè)功能的,下面這篇文章主要給大家介紹了關(guān)于spring入門教程之bean繼承與自動(dòng)裝配的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • java實(shí)現(xiàn)學(xué)生成績(jī)信息管理系統(tǒng)

    java實(shí)現(xiàn)學(xué)生成績(jī)信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)學(xué)生成績(jī)信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • Java常見運(yùn)算符之位運(yùn)算符大全

    Java常見運(yùn)算符之位運(yùn)算符大全

    這篇文章主要介紹了Java常見運(yùn)算符之位運(yùn)算符的相關(guān)資料,包括按位與、按位或、按位異或、按位取反、左移、右移和無符號(hào)右移,這些運(yùn)算符在判斷奇偶、交換變量、計(jì)算絕對(duì)值、判斷是否為2的冪以及快速計(jì)算2的冪次方等方面有廣泛應(yīng)用,需要的朋友可以參考下
    2025-04-04
  • JAVA 根據(jù)設(shè)置的概率生成隨機(jī)數(shù)的方法

    JAVA 根據(jù)設(shè)置的概率生成隨機(jī)數(shù)的方法

    本篇文章主要介紹了JAVA 根據(jù)設(shè)置的概率生成隨機(jī)數(shù)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • Springboot配置管理Externalized?Configuration深入探究

    Springboot配置管理Externalized?Configuration深入探究

    這篇文章主要介紹了Springboot配置管Externalized?Configuration深入探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • 測(cè)試springboot項(xiàng)目出現(xiàn)Test Ignored的解決

    測(cè)試springboot項(xiàng)目出現(xiàn)Test Ignored的解決

    這篇文章主要介紹了測(cè)試springboot項(xiàng)目出現(xiàn)Test Ignored的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 關(guān)于springboot 中使用httpclient或RestTemplate做MultipartFile文件跨服務(wù)傳輸?shù)膯栴}

    關(guān)于springboot 中使用httpclient或RestTemplate做MultipartFile文件跨服務(wù)傳輸

    這篇文章主要介紹了關(guān)于springboot 中使用httpclient或RestTemplate做MultipartFile文件跨服務(wù)傳輸?shù)膯栴},本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • SpringBoot2新特性 自定義端點(diǎn)詳解

    SpringBoot2新特性 自定義端點(diǎn)詳解

    這篇文章主要介紹了SpringBoot2新特性 自定義端點(diǎn)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • SpringBoot配置application.yml時(shí)遇到的錯(cuò)誤及解決

    SpringBoot配置application.yml時(shí)遇到的錯(cuò)誤及解決

    這篇文章主要介紹了SpringBoot配置application.yml時(shí)遇到的錯(cuò)誤及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • MongoDB整合Spring實(shí)例詳細(xì)講解(含代碼)

    MongoDB整合Spring實(shí)例詳細(xì)講解(含代碼)

    這篇文章主要介紹了MongoDB整合Spring實(shí)例詳細(xì)講解(含代碼),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-01-01

最新評(píng)論