Java熱部署相關
阿新 • • 發佈:2018-05-27
cal 下載 copy 信息 sse avi 必須 true tomcat
有了總體實現思路之後,我們可以想到如下幾個需要完成的目標:
1、定義一個用戶自定義應用程序的接口,這是因為,我們需要在容器應用中去加載用戶自定義的應用程序。
2、我們還需要一個配置文件,讓用戶去配置他們的應用程序。
3、應用啟動的時候,加載所有已有的用戶自定義應用程序。
4、為了支持熱部署,我們需要一個監聽器,來監聽應用發布目錄中每個文件的變動。這樣,當某個應用重新部署之後,我們就可以得到通知,進而進行熱部署處理。
實現部分:
首先,我們定義一個接口,每一個用戶自定義的程序中都必須包含唯一一個實現了該接口的類。代碼如下:
[java] view plain copy
在這個例子中,每一個用戶自定義的應用程序,都必須首先打包成一個jar文件,然後發布到一個指定的目錄,按照指定的格式,然後首次發布的時候,還需要將應用的配置添加到配置文件中。所以,首先,我們需要定義一個可以加載指定目錄jar文件的類: [java] view plain copy
這個方法很簡單,就是從多個目錄中掃描jar文件,然後返回一個新的URLClassLoader實例。至於scanJarFiles方法,你可以隨後下載本文的源碼。然後,我們需要定義一個配置文件,用戶需要將他們自定義的應用程序信息配置在這裏,這樣,該容器應用隨後就根據這個配置文件來加載所有的應用程序: [html] view plain copy
這個配置是XML格式的,每一個app標簽就表示一個應用程序,每一個應用程序,需要配置名稱和那個實現了IApplication接口的類的完整路徑和名稱。 有了這個配置文件,我們需要對其進行解析,在這個例子中,我使用的是xstream,很簡單,你可以下載源碼,然後看看就知道了。這裏略過。這裏需要提一下:每個應用的名稱(name),是至關重要的,因為該例子中,我們的發布目錄是整個項目發布目錄下的applications目錄,這是所有用戶自定義應用程序發布的目錄。而用戶發布一個應用程序,需要首先在該目錄下新建一個和這裏配置的name一樣名稱的文件夾,然後將打包好的應用發布到該文件夾中。(你必須這樣做,否則在這個例子中,你會發布失敗)。 好了,現在加載jar的方法和配置都有了,下面將是整個例子的核心部分,對,就是應用程序管理類,這個類就是要完成對每一個用戶自定義應用程序的管理和維護。首先要做的,就是如何加載一個應用程序: [java] view plain copy
可以看到,這個方法接收兩個參數,一個是基本路徑,一個是應用程序配置。基本路徑其實就是項目發布目錄的地址,而AppConfig其實就是配置文件中app標簽的一個實體映射,這個方法從指定的配置目錄中加載指定的類,然後調用該應用的init方法,完成用戶自定義應用程序的初始化。最後將,該加載的應用放入內存中。 現在,所有的準備工作,都已經完成了。接下來,在整個應用程序啟動的時候,我們需要加載所有的用戶自定義應用程序,所以,我們在ApplicationManager中添加一個方法: [java] view plain copy
這個方法,就是將用戶配置的所有應用程序加載到該容器應用中來。好了,現在我們是不是需要寫兩個獨立的應用程序試試效果了,要寫這個應用程序,首先我們新建一個java應用程序,然後引用這個例子項目,或者將該例子項目打包成一個jar文件,然後引用到這個獨立的應用中來,因為這個獨立的應用程序中,必須要包含一個實現了IApplication接口的類。我們來看看這個例子包含的一個獨立應用的樣子: [java] view plain copy
是不是很簡單?對,就是這麽簡單。你可以照這個樣子,再寫一個獨立應用。接下來,你還需要在applications.xml中進行配置,很簡單,就是在apps標簽中增加如下代碼: [html] view plain copy
當某個文件改變的時候,該方法會被回調。所以,我們在這個方法中調用了ApplicationManager的reloadApplication方法,重現加載該應用程序。 [java] view plain copy
重現加載應用程序時,我們首先從內存中刪除該應用程序,然後調用原來應用程序的destory方法,最後按照配置重新創建該應用程序實例。 到這裏,你還覺得熱部署很玄妙很高深嗎?一切就是如此簡單。好了,言歸正傳,為了讓我們自定義的監聽接口可以有效工作起來,我們還需要指定它要監聽的目錄: [java] view plain copy
這裏,就是初始化監聽器的地方,我們使用VFS的DefaultFileMonitor完成監聽。而監聽的目錄,就是應用發布目錄applications。接下來,為了讓整個應用程序可以持續的運行而不會結束,我們修改下啟動方法: [java] view plain copy
好了,到這裏,一切都要結束了。現在,你已經很明白熱部署是怎麽一回事了,對嗎?不明白?OK,還有最後一招,去看看源碼吧! 源碼我已經放到了GitHub上面了,地址:https://github.com/chenjie19891104/ijavaboy/tree/master/AppLoader,歡迎下載使用,你擁有一切的權利對其進行修改。 參考鏈接:https://blog.csdn.net/csy_insist/article/details/52289414 https://blog.csdn.net/chaofanwei2/article/details/51298818
今天發現早年在大象筆記中寫的一篇筆記,之前放在ijavaboy上的,現在它已經訪問不了了。前幾天又有同事在討論這個問題。這裏拿來分享一下。
在web應用開發或者遊戲服務器開發的過程中,我們時時刻刻都在使用熱部署。熱部署的目的很簡單,就是為了節省應用開發和發布的時間。比如,我們在使用Tomcat或者Jboss等應用服務器開發應用時,我們經常會開啟熱部署功能。熱部署,簡單點來說,就是我們將打包好的應用直接替換掉原有的應用,不用關閉或者重啟服務器,一切就是這麽簡單。那麽,熱部署到底是如何實現的呢?在本文中,我將寫一個實例,這個實例就是一個容器應用,允許用戶發布自己的應用,同時支持熱部署。
在Java中,要實現熱部署,首先,你得明白,Java中類的加載方式。每一個應用程序的類都會被ClassLoader加載,所以,要實現一個支持熱部署的應用,我們可以對每一個用戶自定義的應用程序使用一個單獨的ClassLoader進行加載。然後,當某個用戶自定義的應用程序發生變化的時候,我們首先銷毀原來的應用,然後使用一個新的ClassLoader來加載改變之後的應用。而所有其他的應用程序不會受到一點幹擾。先看一下,該應用的設計圖:- public interface IApplication {
- public void init();
- public void execute();
- public void destory();
- }
在這個例子中,每一個用戶自定義的應用程序,都必須首先打包成一個jar文件,然後發布到一個指定的目錄,按照指定的格式,然後首次發布的時候,還需要將應用的配置添加到配置文件中。所以,首先,我們需要定義一個可以加載指定目錄jar文件的類: [java]
- public ClassLoader createClassLoader(ClassLoader parentClassLoader, String... folders) {
- List<URL> jarsToLoad = new ArrayList<URL>();
- for (String folder : folders) {
- List<String> 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);
- }
這個方法很簡單,就是從多個目錄中掃描jar文件,然後返回一個新的URLClassLoader實例。至於scanJarFiles方法,你可以隨後下載本文的源碼。然後,我們需要定義一個配置文件,用戶需要將他們自定義的應用程序信息配置在這裏,這樣,該容器應用隨後就根據這個配置文件來加載所有的應用程序: [html] view plain copy
- <apps>
- <app>
- <name> TestApplication1</name >
- <file> com.ijavaboy.app.TestApplication1</file >
- </app>
- <app>
- <name> TestApplication2</name >
- <file> com.ijavaboy.app.TestApplication2</file >
- </app>
- </apps>
這個配置是XML格式的,每一個app標簽就表示一個應用程序,每一個應用程序,需要配置名稱和那個實現了IApplication接口的類的完整路徑和名稱。 有了這個配置文件,我們需要對其進行解析,在這個例子中,我使用的是xstream,很簡單,你可以下載源碼,然後看看就知道了。這裏略過。這裏需要提一下:每個應用的名稱(name),是至關重要的,因為該例子中,我們的發布目錄是整個項目發布目錄下的applications目錄,這是所有用戶自定義應用程序發布的目錄。而用戶發布一個應用程序,需要首先在該目錄下新建一個和這裏配置的name一樣名稱的文件夾,然後將打包好的應用發布到該文件夾中。(你必須這樣做,否則在這個例子中,你會發布失敗)。 好了,現在加載jar的方法和配置都有了,下面將是整個例子的核心部分,對,就是應用程序管理類,這個類就是要完成對每一個用戶自定義應用程序的管理和維護。首先要做的,就是如何加載一個應用程序: [java] view plain copy
- public void createApplication(String basePath, AppConfig config){
- String folderName = basePath + GlobalSetting. JAR_FOLDER + config.getName();
- 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 e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
可以看到,這個方法接收兩個參數,一個是基本路徑,一個是應用程序配置。基本路徑其實就是項目發布目錄的地址,而AppConfig其實就是配置文件中app標簽的一個實體映射,這個方法從指定的配置目錄中加載指定的類,然後調用該應用的init方法,完成用戶自定義應用程序的初始化。最後將,該加載的應用放入內存中。 現在,所有的準備工作,都已經完成了。接下來,在整個應用程序啟動的時候,我們需要加載所有的用戶自定義應用程序,所以,我們在ApplicationManager中添加一個方法: [java] view plain copy
- public void loadAllApplications(String basePath){
- for(AppConfig config : this.configManager.getConfigs()){
- this.createApplication(basePath, config);
- }
- }
這個方法,就是將用戶配置的所有應用程序加載到該容器應用中來。好了,現在我們是不是需要寫兩個獨立的應用程序試試效果了,要寫這個應用程序,首先我們新建一個java應用程序,然後引用這個例子項目,或者將該例子項目打包成一個jar文件,然後引用到這個獨立的應用中來,因為這個獨立的應用程序中,必須要包含一個實現了IApplication接口的類。我們來看看這個例子包含的一個獨立應用的樣子: [java] view plain copy
- public class TestApplication1 implements IApplication{
- @Override
- public void init() {
- System. out.println("TestApplication1-->init" );
- }
- @Override
- public void execute() {
- System. out.println("TestApplication1-->do something" );
- }
- @Override
- public void destory() {
- System. out.println("TestApplication1-->destoryed" );
- }
- }
是不是很簡單?對,就是這麽簡單。你可以照這個樣子,再寫一個獨立應用。接下來,你還需要在applications.xml中進行配置,很簡單,就是在apps標簽中增加如下代碼: [html] view plain copy
- <app>
- <name> TestApplication1</name >
- <file> com.ijavaboy.app.TestApplication1</file >
- </app>
- public void fileChanged (FileChangeEvent event) throws Exception {
- String ext = event.getFile().getName().getExtension();
- if(!"jar" .equalsIgnoreCase(ext)){
- return;
- }
- String name = event.getFile().getName().getParent().getBaseName();
- ApplicationManager. getInstance().reloadApplication(name);
當某個文件改變的時候,該方法會被回調。所以,我們在這個方法中調用了ApplicationManager的reloadApplication方法,重現加載該應用程序。 [java] view plain copy
- public void reloadApplication (String name){
- IApplication oldApp = this.apps .remove(name);
- if(oldApp == null){
- return;
- }
- oldApp.destory(); //call the destroy method in the user‘s application
- AppConfig config = this.configManager .getConfig(name);
- if(config == null){
- return;
- }
- createApplication(getBasePath(), config);
重現加載應用程序時,我們首先從內存中刪除該應用程序,然後調用原來應用程序的destory方法,最後按照配置重新創建該應用程序實例。 到這裏,你還覺得熱部署很玄妙很高深嗎?一切就是如此簡單。好了,言歸正傳,為了讓我們自定義的監聽接口可以有效工作起來,我們還需要指定它要監聽的目錄: [java] view plain copy
- public void initMonitorForChange(String basePath){
- try {
- this.fileManager = VFS.getManager();
- File file = new File(basePath + GlobalSetting.JAR_FOLDER);
- FileObject monitoredDir = this.fileManager .resolveFile(file.getAbsolutePath());
- FileListener fileMonitorListener = new JarFileChangeListener();
- this.fileMonitor = new DefaultFileMonitor(fileMonitorListener);
- this.fileMonitor .setRecursive(true);
- this.fileMonitor .addFile(monitoredDir);
- this.fileMonitor .start();
- System. out.println("Now to listen " + monitoredDir.getName().getPath());
- } catch (FileSystemException e) {
- e.printStackTrace();
- }
- }
這裏,就是初始化監聽器的地方,我們使用VFS的DefaultFileMonitor完成監聽。而監聽的目錄,就是應用發布目錄applications。接下來,為了讓整個應用程序可以持續的運行而不會結束,我們修改下啟動方法: [java] view plain copy
- 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();
- }
- }
- }
好了,到這裏,一切都要結束了。現在,你已經很明白熱部署是怎麽一回事了,對嗎?不明白?OK,還有最後一招,去看看源碼吧! 源碼我已經放到了GitHub上面了,地址:https://github.com/chenjie19891104/ijavaboy/tree/master/AppLoader,歡迎下載使用,你擁有一切的權利對其進行修改。 參考鏈接:https://blog.csdn.net/csy_insist/article/details/52289414 https://blog.csdn.net/chaofanwei2/article/details/51298818
Java熱部署相關