public class DyLoader extends ClassLoader { public DyLoader() { super(DyLoader.class.getClassLoader()); }
public Class loadFromCustomRepository(String className) { /**取环境变量*/ String classPath = System.getProperty("java.class.path"); List classRepository = new ArrayList(); /**取得该路径下的所有文件夹 */ if ( (classPath != null) && ! (classPath.equals(""))) { StringTokenizer tokenizer = new StringTokenizer(classPath, File.pathSeparator); while (tokenizer.hasMoreTokens()) { classRepository.add(tokenizer.nextToken()); } } Iterator dirs = classRepository.iterator(); byte[] classBytes = null; /**在类路径上查找该名称的类是否存在,如果不存在继续查找*/ while (dirs.hasNext()) { String dir = (String) dirs.next(); //replace '.' in the class name with File.separatorChar & append .class to the name String classFileName = className.replace('.', File.separatorChar); classFileName += ".class"; try { File file = new File(dir + File.separatorChar + classFileName); if (file.exists()) { InputStream is = new FileInputStream(file); /**把文件读到字节文件*/ classBytes = new byte[is.available()]; is.read(classBytes); break; } } catch (IOException ex) { System.out.println("IOException raised while reading class file data"); ex.printStackTrace(); return null; } } return this.defineClass(className, classBytes, 0, classBytes.length);//加载类 }
}
如下调用 DyLoader loader = new DyLoader(); Class a = loader.loadFromCustomRepository("com.lijz.SampleDyService"); Class b = loader.loadFromCustomRepository("com.lijz.SampleDyService"); 第三行代码将会抛出 java.lang.LinkageError: duplicate class definition: com/lijz/SampleDyService
如果如下调用,则一切正常,这是因为你使用新的ClassLoader实例来装载com.lijz.SampleDyService" DyLoader loader= new DyLoader(); Class a loader.loadFromCustomRepository("com.lijz.SampleDyService"); DyLoader newLoader = new DyLoader(); Class b = newLoader.loadFromCustomRepository("com.lijz.SampleDyService");
言归正传,停止介绍Classloader,回到利用Classloader来避免重新启动你的应用程序
首先定义业务逻辑处理模块接口
public interface IDyService { public void start(); public void close(); public void doBusiness(); }
start方法用于初始化,close用于清除此服务。doBusiness用来模拟处理业务
一个实现的例子如下: public class SampleDyService implements IDyService { public SampleDyService() { } public void doBusiness() { System.out.println("hello boy"); } public void start() { System.out.println("Start SampleDyService:"); System.out.println(SampleDyService.class.getClassLoader()); }
public void close() { System.out.println("close SampleDyService:"); } }
public class Main() { private IDyService service = null; public Main() throws Exception { DyLoader loader = new DyLoader(); service = (IDyService) loader.loadFromCustomRepository( "com.gctech.service.test.dyloader.SampleDyService").newInstance(); service.start();
while (true) {
service.doBusiness(); Thread.sleep(1000 * 3); } }
public static void main(String[] args) throws Exception { Main main = new Main(); }
}
假设业务逻辑改变,要求SampleDyService的doBusiness打印出"hello girl"。新编译好的SampleDyService已经覆盖了原来的类。在不启动应用程序前提条件下如何更新新的业务逻辑呢? 分俩部来完成 第一步,在Main类里添加notifyReLoad方法,用新的classloader重新生成SampleDyService实例 public void notifyReLoad() throws Exception { service.close(); DyLoader loader = new DyLoader(); service = (IDyService) loader.loadFromCustomRepository( "com.gctech.service.test.dyloader.SampleDyService").newInstance(); service.start();
}
第二步:使用某种机制检测来检测SampleDyService.class已经改变,如可以通过上面的例子启动一个线程检测SampleDyService.class是否被改变,如果改变则调用Main.notifyReLoad().也可以采用主动通知方式,如web应用中,提供这样的界面调用。在此例子中提供一个检测线程。 public class DyServciceChecker extends Thread { Main main = null;
public DyServciceChecker() { } public void setMain(Main main) { this.main = main;