今天發現早年在大象筆記中寫的一篇筆記,之前放在ijavaboy上的,現在它已經訪問不了了。前幾天又有同事在討論這個問題。這裏拿來分享一下。
在web應用開發或者遊戲服務器開發的過程中,我們時時刻刻都在使用熱部署。熱部署的目的很簡單,就是爲了節省應用開發和發佈的時間。比如,我們在使用Tomcat或者Jboss等應用服務器開發應用時,我們經常會開啓熱部署功能。熱部署,簡單點來說,就是我們將打包好的應用直接替換掉原有的應用,不用關閉或者重啓服務器,一切就是這麼簡單。那麼,熱部署到底是如何實現的呢?在本文中,我將寫一個實例,這個實例就是一個容器應用,允許用戶發佈自己的應用,同時支持熱部署。
public interface IApplication {
public void init();
public void execute();
public void destory();
}
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);
}
<apps>
<app>
<name> TestApplication1</name >
<file> com.ijavaboy.app.TestApplication1</file >
</app>
<app>
<name> TestApplication2</name >
<file> com.ijavaboy.app.TestApplication2</file >
</app>
</apps>
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();
}
}
public void loadAllApplications(String basePath){
for(AppConfig config : this.configManager.getConfigs()){
this.createApplication(basePath, config);
}
}
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" );
}
}
<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);
}
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);
}
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();
}
}
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();
}
}
}