今天发现早年在大象笔记中写的一篇笔记,之前放在ijavaboy上的,现在它已经访问不了了。前几天又有同事在讨论这个问题。这里拿来分享一下。
在web应用开发或者游戏服务器开发的过程中,我们时时刻刻都在使用热部署。热部署的目的很简单,就是为了节省应用开发和发布的时间。比如,我们在使用Tomcat或者Jboss等应用服务器开发应用时,我们经常会开启热部署功能。热部署,简单点来说,就是我们将打包好的应用直接替换掉原有的应用,不用关闭或者重启服务器,一切就是这么简单。那么,热部署到底是如何实现的呢?在本文中,我将写一个实例,这个实例就是一个容器应用,允许用户发布自己的应用,同时支持热部署。
public interface IApplication {
public void init();
public void execute();
public void destory();
}
public ClassLoader createClassLoader(ClassLoader parentClassLoader, String... folders) {
List jarsToLoad = new ArrayList();
for (String folder : folders) {
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);
}
TestApplication1
com.ijavaboy.app.TestApplication1
TestApplication2
com.ijavaboy.app.TestApplication2
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" );
}
}
TestApplication1
com.ijavaboy.app.TestApplication1
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();
}
}
}