1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        徒手?jǐn)]一個(gè)熱部署插件!

        共 5621字,需瀏覽 12分鐘

         ·

        2021-11-18 02:00

        你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

        你來(lái),我們一起精進(jìn)!你不來(lái),我和你的競(jìng)爭(zhēng)對(duì)手一起精進(jìn)!

        編輯:業(yè)余草

        juejin.cn/post/7031051782939738125

        推薦:https://www.xttblog.com/?p=5290

        引言

        在項(xiàng)目開(kāi)發(fā)中,每次修改文件就需要重啟一次代碼,這樣太浪費(fèi)時(shí)間了,所以在IDEA中使用JRebel插件實(shí)現(xiàn)項(xiàng)目??熱部署,可自動(dòng)熱部署,無(wú)需重啟項(xiàng)目。雖然一直清楚熱部署是打破雙親委派來(lái)實(shí)現(xiàn)的,但是一直沒(méi)有手寫(xiě)過(guò)熱部署代碼,今天寫(xiě)一次。??

        雙親委派機(jī)制

        了解熱部署之前,首先需要知道什么是雙親委派,在 IDE 中寫(xiě)的代碼最終經(jīng)過(guò)編譯器會(huì)形成 .class 文件,由 classLoader 加載到 JVM 中執(zhí)行。

        JVM 中提供了三層的 ClassLoader:

        • Bootstrap classLoader:主要負(fù)責(zé)加載核心的類(lèi)庫(kù)(java.lang.*等),構(gòu)造ExtClassLoader和APPClassLoader。
        • ExtClassLoader:主要負(fù)責(zé)加載jre/lib/ext目錄下的一些擴(kuò)展的jar。
        • AppClassLoader:主要負(fù)責(zé)加載應(yīng)用程序的主函數(shù)類(lèi)

        加載過(guò)程圖如下:

        雙親委派機(jī)制

        實(shí)現(xiàn)熱部署思路

        一個(gè)類(lèi)一旦被JVM加載過(guò),就不會(huì)再次被加載。想實(shí)現(xiàn)熱部署,就需要在.class文件修改后,由classLoader重新加載修改的.class文件。對(duì).class文件做監(jiān)聽(tīng),一旦文件修改,則重新加載類(lèi)。

        在此實(shí)現(xiàn)中用一個(gè)Map模擬JVM已經(jīng)加載過(guò)的.class文件,當(dāng)監(jiān)聽(tīng)到文件內(nèi)容修改之后,移除Map中舊的.class文件,將新的.class文件加載并存放至Map中,調(diào)用init方法,執(zhí)行初始化動(dòng)作,模擬.class文件已經(jīng)加載到JVM虛擬機(jī)中。

        下面講代碼實(shí)現(xiàn)!

        pom文件:


        <project?xmlns="http://maven.apache.org/POM/4.0.0"
        ?????????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        ?????????xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/xsd/maven-4.0.0.xsd">

        ????<modelVersion>4.0.0modelVersion>

        ????<groupId>com.hanhanggroupId>
        ????<artifactId>hotCodeartifactId>
        ????<version>1.0-SNAPSHOTversion>
        ????<dependencies>
        ????????<dependency>
        ????????????<groupId>org.projectlombokgroupId>
        ????????????<artifactId>lombokartifactId>
        ????????????<version>1.18.22version>
        ????????????<scope>compilescope>
        ????????dependency>
        ????????<dependency>
        ????????????<groupId>log4jgroupId>
        ????????????<artifactId>log4jartifactId>
        ????????????<version>1.2.17version>
        ????????dependency>
        ????????<dependency>
        ????????????<groupId>org.slf4jgroupId>
        ????????????<artifactId>slf4j-log4j12artifactId>
        ????????????<version>1.7.26version>
        ????????dependency>
        ????????<dependency>
        ????????????<groupId>org.slf4jgroupId>
        ????????????<artifactId>slf4j-apiartifactId>
        ????????????<version>1.7.26version>
        ????????dependency>
        ????????<dependency>
        ????????????<groupId>org.apache.commonsgroupId>
        ????????????<artifactId>commons-vfs2artifactId>
        ????????????<version>2.9.0version>
        ????????dependency>
        ????????<dependency>
        ????????????<groupId>com.thoughtworks.xstreamgroupId>
        ????????????<artifactId>xstreamartifactId>
        ????????????<version>1.4.18version>
        ????????dependency>
        ????dependencies>

        ????<properties>
        ????????<maven.compiler.source>8maven.compiler.source>
        ????????<maven.compiler.target>8maven.compiler.target>
        ????properties>

        project>

        IApplication接口

        定義IApplication接口,所有監(jiān)聽(tīng)的類(lèi)都實(shí)現(xiàn)自這個(gè)接口。

        public?interface?IApplication?{
        ????/**
        ?????*?初始化
        ?????*/

        ????void?init();

        ????/**
        ?????*?執(zhí)行
        ?????*/

        ????void?execute();

        ????/**
        ?????*?銷(xiāo)毀
        ?????*/

        ????void?destroy();
        }

        TestApplication1

        監(jiān)聽(tīng)加載的類(lèi)。

        public?class?TestApplication1?implements?IApplication?{
        ????@Override
        ????public?void?init()?{
        ????????System.out.println("TestApplication1--》3");
        ????}

        ????@Override
        ????public?void?execute()?{
        ????????System.out.println("TestApplication1--》execute");
        ????}

        ????@Override
        ????public?void?destroy()?{
        ????????System.out.println("TestApplication1--》destroy");
        ????}
        }

        IClassLoader

        類(lèi)加載器,實(shí)現(xiàn)通過(guò)包掃描類(lèi)的功能。

        public?interface?IClassLoader?{
        ????/**
        ?????*?創(chuàng)建classLoader
        ?????*?@param?parentClassLoader?父classLoader
        ?????*?@param?paths?路徑
        ?????*?@return?類(lèi)加載器
        ?????*/

        ????ClassLoader?createClassLoader(ClassLoader?parentClassLoader,?String...paths);
        }

        SimpleJarLoader

        public?class?SimpleJarLoader?implements?IClassLoader?{
        ????@Override
        ????public?ClassLoader?createClassLoader(ClassLoader?parentClassLoader,?String...?paths)?{
        ????????List?jarsToLoad?=?new?ArrayList<>();
        ????????for?(String?folder?:?paths)?{
        ????????????List?jarPaths?=?scanJarFiles(folder);

        ????????????for?(String?jar?:?jarPaths)?{

        ????????????????try?{
        ????????????????????File?file?=?new?File(jar);
        ????????????????????jarsToLoad.add(file.toURI().toURL());

        ????????????????}?catch?(MalformedURLException?e)?{
        ????????????????????e.printStackTrace();
        ????????????????}
        ????????????}
        ????????}

        ????????URL[]?urls?=?new?URL[jarsToLoad.size()];
        ????????jarsToLoad.toArray(urls);

        ????????return?new?URLClassLoader(urls,?parentClassLoader);
        ????}

        ????/**
        ?????*?掃描文件
        ?????*?@param?folderPath?文件路徑
        ?????*?@return?文件列表
        ?????*/

        ????private?List?scanJarFiles(String?folderPath)?{

        ????????List?jars?=?new?ArrayList<>();
        ????????File?folder?=?new?File(folderPath);
        ????????if?(!folder.isDirectory())?{
        ????????????throw?new?RuntimeException("掃描的路徑不存在,?path:"?+?folderPath);
        ????????}

        ????????for?(File?f?:?Objects.requireNonNull(folder.listFiles()))?{
        ????????????if?(!f.isFile())?{
        ????????????????continue;
        ????????????}
        ????????????String?name?=?f.getName();

        ????????????if?(name.length()?==?0)?{
        ????????????????continue;
        ????????????}

        ????????????int?extIndex?=?name.lastIndexOf(".");
        ????????????if?(extIndex?0)?{
        ????????????????continue;
        ????????????}

        ????????????String?ext?=?name.substring(extIndex);
        ????????????if?(!".jar".equalsIgnoreCase(ext))?{
        ????????????????continue;
        ????????????}

        ????????????jars.add(folderPath?+?"/"?+?name);
        ????????}
        ????????return?jars;
        ????}
        }

        AppConfigList配置類(lèi)

        @Data
        public?class?AppConfigList?{
        ????private?List?configs;

        ????@Data
        ????public?static?class?AppConfig{
        ????????private?String?name;

        ????????private?String?file;
        ????}
        }

        GlobalSetting 全局配置類(lèi)

        public?class?GlobalSetting?{
        ????public?static?final?String?APP_CONFIG_NAME?=?"application.xml";
        ????public?static?final?String?JAR_FOLDER?=?"com/hanhang/app/";
        }

        application.xml配置

        通過(guò)xml配置加后面的解析,確定監(jiān)聽(tīng)那個(gè)class文件。

        <apps>
        ????<app>
        ????????<name>TestApplication1name>
        ????????<file>com.hanhang.app.TestApplication1file>
        ????app>
        apps>

        JarFileChangeListener 監(jiān)聽(tīng)器

        public?class?JarFileChangeListener?implements?FileListener?{
        ????@Override
        ????public?void?fileCreated(FileChangeEvent?fileChangeEvent)?throws?Exception?{
        ????????String?name?=?fileChangeEvent.getFileObject().getName().getBaseName().replace(".class","");

        ????????ApplicationManager.getInstance().reloadApplication(name);
        ????}

        ????@Override
        ????public?void?fileDeleted(FileChangeEvent?fileChangeEvent)?throws?Exception?{
        ????????String?name?=?fileChangeEvent.getFileObject().getName().getBaseName().replace(".class","");

        ????????ApplicationManager.getInstance().reloadApplication(name);
        ????}

        ????@Override
        ????public?void?fileChanged(FileChangeEvent?fileChangeEvent)?throws?Exception?{
        ????????String?name?=?fileChangeEvent.getFileObject().getName().getBaseName().replace(".class","");

        ????????ApplicationManager.getInstance().reloadApplication(name);

        ????}
        }

        AppConfigManager

        此類(lèi)為config的管理類(lèi),用于加載配置。

        public?class?AppConfigManager?{
        ????private?final?List?configs;

        ????public?AppConfigManager(){
        ????????configs?=?new?ArrayList<>();
        ????}

        ????/**
        ?????*?加載配置
        ?????*?@param?path?路徑
        ?????*/

        ????public?void?loadAllApplicationConfigs(URI?path){

        ????????File?file?=?new?File(path);
        ????????XStream?xstream?=?getXmlDefine();
        ????????try?{
        ????????????AppConfigList?configList?=?(AppConfigList)xstream.fromXML(new?FileInputStream(file));

        ????????????if(configList.getConfigs()?!=?null){
        ????????????????this.configs.addAll(new?ArrayList<>(configList.getConfigs()));
        ????????????}

        ????????}?catch?(FileNotFoundException?e)?{
        ????????????e.printStackTrace();
        ????????}

        ????}

        ????/**
        ?????*?獲取xml配置定義
        ?????*?@return?XStream
        ?????*/

        ????private?XStream?getXmlDefine(){
        ????????XStream?xstream?=?new?XStream(new?DomDriver());
        ????????xstream.alias("apps",?AppConfigList.class);
        ????????xstream.alias("app",?AppConfigList.AppConfig.class);
        ????????xstream.aliasField("name",?AppConfigList.AppConfig.class,?"name");
        ????????xstream.aliasField("file",?AppConfigList.AppConfig.class,?"file");
        ????????xstream.addImplicitCollection(AppConfigList.class,?"configs");
        ????????Class[]?classes?=?new?Class[]?{AppConfigList.class,AppConfigList.AppConfig.class};
        ????????xstream.allowTypes(classes);
        ????????return?xstream;
        ????}

        ????public?final?List?getConfigs()?{
        ????????return?configs;
        ????}

        ????public?AppConfigList.AppConfig?getConfig(String?name){
        ????????for(AppConfigList.AppConfig?config?:?this.configs){
        ????????????if(config.getName().equalsIgnoreCase(name)){
        ????????????????return?config;
        ????????????}
        ????????}
        ????????return?null;
        ????}
        }

        ApplicationManager

        此類(lèi)管理已經(jīng)在Map中加載的類(lèi),并且添加監(jiān)聽(tīng)器,實(shí)現(xiàn)class文件修改后的監(jiān)聽(tīng)重新加載工作。

        public?class?ApplicationManager?{
        ????private?static?ApplicationManager?instance;

        ????private?IClassLoader?jarLoader;
        ????private?AppConfigManager?configManager;

        ????private?Map?apps;

        ????private?ApplicationManager(){
        ????}

        ????public?void?init(){
        ????????jarLoader?=?new?SimpleJarLoader();
        ????????configManager?=?new?AppConfigManager();
        ????????apps?=?new?HashMap<>();

        ????????initAppConfigs();

        ????????URL?basePath?=?this.getClass().getClassLoader().getResource("");

        ????????loadAllApplications(Objects.requireNonNull(basePath).getPath());

        ????????initMonitorForChange(basePath.getPath());
        ????}

        ????/**
        ?????*?初始化配置
        ?????*/

        ????public?void?initAppConfigs(){

        ????????try?{
        ????????????URL?path?=?this.getClass().getClassLoader().getResource(GlobalSetting.APP_CONFIG_NAME);
        ????????????configManager.loadAllApplicationConfigs(Objects.requireNonNull(path).toURI());
        ????????}?catch?(URISyntaxException?e)?{
        ????????????e.printStackTrace();
        ????????}
        ????}

        ????/**
        ?????*?加載類(lèi)
        ?????*?@param?basePath?根目錄
        ?????*/

        ????public?void?loadAllApplications(String?basePath){

        ????????for(AppConfigList.AppConfig?config?:?this.configManager.getConfigs()){
        ????????????this.createApplication(basePath,?config);
        ????????}
        ????}

        ????/**
        ?????*?初始化監(jiān)聽(tīng)器
        ?????*?@param?basePath?路徑
        ?????*/

        ????public?void?initMonitorForChange(String?basePath){
        ????????try?{
        ????????????FileSystemManager?fileManager?=?VFS.getManager();

        ????????????File?file?=?new?File(basePath?+?GlobalSetting.JAR_FOLDER);
        ????????????FileObject?monitoredDir?=?fileManager.resolveFile(file.getAbsolutePath());
        ????????????FileListener?fileMonitorListener?=?new?JarFileChangeListener();
        ????????????DefaultFileMonitor?fileMonitor?=?new?DefaultFileMonitor(fileMonitorListener);
        ????????????fileMonitor.setRecursive(true);
        ????????????fileMonitor.addFile(monitoredDir);
        ????????????fileMonitor.start();
        ????????????System.out.println("Now?to?listen?"?+?monitoredDir.getName().getPath());

        ????????}?catch?(FileSystemException?e)?{
        ????????????e.printStackTrace();
        ????????}
        ????}

        ????/**
        ?????*?根據(jù)配置加載類(lèi)
        ?????*?@param?basePath?路徑
        ?????*?@param?config?配置
        ?????*/

        ????public?void?createApplication(String?basePath,?AppConfigList.AppConfig?config){
        ????????String?folderName?=?basePath?+?GlobalSetting.JAR_FOLDER;
        ????????ClassLoader?loader?=?this.jarLoader.createClassLoader(ApplicationManager.class.getClassLoader(),?folderName);

        ????????try?{
        ????????????Class?appClass?=?loader.loadClass(config.getFile());

        ????????????IApplication?app?=?(IApplication)appClass.newInstance();

        ????????????app.init();

        ????????????this.apps.put(config.getName(),?app);

        ????????}?catch?(ClassNotFoundException?|?InstantiationException?|?IllegalAccessException?e)?{
        ????????????e.printStackTrace();
        ????????}
        ????}

        ????/**
        ?????*?重新加載
        ?????*?@param?name?類(lèi)名
        ?????*/

        ????public?void?reloadApplication(String?name){
        ????????IApplication?oldApp?=?this.apps.remove(name);

        ????????if(oldApp?==?null){
        ????????????return;
        ????????}

        ????????oldApp.destroy();

        ????????AppConfigList.AppConfig?config?=?this.configManager.getConfig(name);
        ????????if(config?==?null){
        ????????????return;
        ????????}

        ????????createApplication(getBasePath(),?config);
        ????}

        ????public?static?ApplicationManager?getInstance(){
        ????????if(instance?==?null){
        ????????????instance?=?new?ApplicationManager();
        ????????}
        ????????return?instance;
        ????}

        ????/**
        ?????*?獲取類(lèi)
        ?????*?@param?name?類(lèi)名
        ?????*?@return?緩存中的類(lèi)
        ?????*/

        ????public?IApplication?getApplication(String?name){
        ????????if(this.apps.containsKey(name)){
        ????????????return?this.apps.get(name);
        ????????}
        ????????return?null;
        ????}

        ????public?String?getBasePath(){
        ????????return?Objects.requireNonNull(this.getClass().getClassLoader().getResource("")).getPath();
        ????}
        }

        MainTest

        測(cè)試類(lèi),創(chuàng)建一個(gè)線程,讓程序一直監(jiān)聽(tīng)文件修改。

        public?static?void?main(String[]?args){

        ????Thread?t?=?new?Thread(new?Runnable()?{

        ????????@Override
        ????????public?void?run()?{
        ????????????ApplicationManager?manager?=?ApplicationManager.getInstance();
        ????????????manager.init();
        ????????}
        ????});

        ????t.start();

        ????while(true){
        ????????try?{
        ????????????Thread.sleep(300);
        ????????}?catch?(InterruptedException?e)?{
        ????????????e.printStackTrace();
        ????????}
        ????}
        }

        代碼演示

        程序啟動(dòng)后,控制臺(tái)輸出。

        代碼演示

        TestApplication1的 init 方法修改為:

        @Override
        public?void?init()?{
        ????System.out.println("TestApplication1--》300");
        }

        重新build項(xiàng)目,控制臺(tái)輸出如下:

        熱發(fā)布、熱部署

        此時(shí),TestApplication1已經(jīng)重新加載。以上就是我實(shí)現(xiàn)??熱部署的關(guān)鍵代碼,如需完整代碼,加微信:xttblog2,免費(fèi)獲??!

        瀏覽 74
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            天天日天天操天天舔 | 天天操欧美 | 中文字幕一区二区二区三级片 | 日本高清熟妇毛茸茸视频 | 国产视频久久久久久 | 把女的下面扒开添视频 | 欧美性爱天天看 | 女人高潮无遮挡免费视频 | 黄色片网站在线观看 | 日韩一级a|