我們大家都知道,每個運行中的線程都有一個成員contextClassLoader,用來在運行時動態地載入其它類。
系統預設的contextClassLoader是systemClassLoader,所以一般而言java程式在執行時可以使用JVM內建的類、$JAVA_HOME/jre/lib/ext/中的類和$CLASSPATH/中的類,對於非預設的jar,一般只能手動在配置環境添加。
但事實上,我們可以通過Thread.currentThread().setContextClassLoader()更改當前線程的contextClassLoader行為,實現在程式內載入外部jar。
PS:
ClassLoader的工作原理是:
1) 線程需要用到某個類時,contextClassLoader將被請求來載入該類
2) contextClassLoader請求它的父ClassLoader來完成該載入請求
3) 如果父ClassLoader無法載入類,則contextClassLoader試圖自己來載入
package org.loon.framework.jar;</p><p>/** *//**<br /> * <p>Title: LoonFramework</p><br /> * <p>Description:JarLoader,用於jar包的外部操作</p><br /> * <p>Copyright: Copyright (c) 2007</p><br /> * <p>Company: LoonFramework</p><br /> * @author chenpeng<br /> * @email:ceponline@yahoo.com.cn<br /> * @version 0.1<br /> */<br />import java.io.BufferedInputStream;<br />import java.io.ByteArrayInputStream;<br />import java.io.ByteArrayOutputStream;<br />import java.io.File;<br />import java.io.FileInputStream;<br />import java.io.FileNotFoundException;<br />import java.io.IOException;<br />import java.io.InputStream;<br />import java.lang.reflect.Method;<br />import java.net.MalformedURLException;<br />import java.net.URL;<br />import java.util.ArrayList;<br />import java.util.HashMap;<br />import java.util.Hashtable;<br />import java.util.jar.JarEntry;<br />import java.util.jar.JarInputStream;<br />import java.util.jar.Manifest;</p><p>public class JarLoader extends ClassLoader {<br /> //資源緩衝<br /> public static Hashtable resources = new Hashtable();</p><p> public static JarLoader loader = new JarLoader();</p><p> public static Class load(byte[] resource) throws Exception {<br /> // 主函數所在類全稱<br /> String mainClassName = "";<br /> //class資源及實體緩衝<br /> ArrayList classNames = new ArrayList();<br /> ArrayList classBuffers = new ArrayList();<br /> // 儲存依賴類<br /> HashMap depends = new HashMap();<br /> // 將byte[]轉為JarInputStream<br /> JarInputStream jar = new JarInputStream(new ByteArrayInputStream(<br /> resource));<br /> Manifest manifest = jar.getManifest();<br /> // 當Main-Class被聲明時,獲得主函數所在類全稱<br /> if (manifest != null) {<br /> mainClassName = manifest.getMainAttributes().getValue("Main-Class");<br /> }<br /> // 依次獲得對應JAR檔案中封裝的各個被壓縮檔的JarEntry<br /> JarEntry entry;<br /> while ((entry = jar.getNextJarEntry()) != null) {<br /> // 當找到的entry為class時<br /> if (entry.getName().toLowerCase().endsWith(".class")) {<br /> // 將類路徑轉變為類全稱<br /> String name = entry.getName().substring(0,<br /> entry.getName().length() - ".class".length()).replace(<br /> '/', '.');<br /> // 載入該類<br /> byte[] data = getResourceData(jar);<br /> // 緩衝類名及資料<br /> classNames.add(name);<br /> classBuffers.add(data);</p><p> } else {<br /> // 非class結尾但開頭字元為'/'時<br /> if (entry.getName().charAt(0) == '/') {<br /> resources.put(entry.getName(), getResourceData(jar));<br /> // 否則追加'/'後緩衝<br /> } else {<br /> resources.put("/" + entry.getName(), getResourceData(jar));<br /> }<br /> }<br /> }<br /> //當獲得的main-class名不為空白時<br /> while (classNames.size() > 0) {<br /> //獲得類路徑全長<br /> int n = classNames.size();<br /> for (int i = classNames.size() - 1; i >= 0; i--) {<br /> try {<br /> //查詢指定類<br /> loader.defineClass((String) classNames.get(i),<br /> (byte[]) classBuffers.get(i), 0,<br /> ((byte[]) classBuffers.get(i)).length);<br /> //獲得類名<br /> String pkName = (String) classNames.get(i);<br /> if (pkName.lastIndexOf('.') >= 0) {<br /> pkName = pkName<br /> .substring(0, pkName.lastIndexOf('.'));<br /> if (loader.getPackage(pkName) == null) {<br /> loader.definePackage(pkName, null, null, null,<br /> null, null, null, null);<br /> }<br /> }<br /> //查詢後刪除緩衝<br /> classNames.remove(i);<br /> classBuffers.remove(i);<br /> } catch (NoClassDefFoundError e) {<br /> depends.put((String) classNames.get(i), e.getMessage()<br /> .replaceAll("/", "."));<br /> } catch (UnsupportedClassVersionError e) {<br /> //jre版本錯誤提示<br /> throw new UnsupportedClassVersionError(classNames.get(i)<br /> + ", " + System.getProperty("java.vm.name") + " "<br /> + System.getProperty("java.vm.version") + ")");<br /> }<br /> }<br /> if (n == classNames.size()) {<br /> for (int i = 0; i < classNames.size(); i++) {<br /> System.err.println("NoClassDefFoundError:"<br /> + classNames.get(i));<br /> String className = (String) classNames.get(i);<br /> while (depends.containsKey(className)) {<br /> className = (String) depends.get(className);<br /> }<br /> }<br /> break;<br /> }<br /> }<br /> try {<br /> //載入<br /> Thread.currentThread().setContextClassLoader(loader);<br /> // 獲得指定類,尋找其他類方式相仿<br /> return Class.forName(mainClassName, true, loader);<br /> } catch (ClassNotFoundException e) {<br /> String className = mainClassName;<br /> while (depends.containsKey(className)) {<br /> className = (String) depends.get(className);<br /> }<br /> throw new ClassNotFoundException(className);<br /> }<br /> }</p><p> /** *//**<br /> * 獲得指定路徑檔案的byte[]形式<br /> * @param name<br /> * @return<br /> */<br /> final static public byte[] getDataSource(String name) {<br /> FileInputStream fileInput;<br /> try {<br /> fileInput = new FileInputStream(new File(name));<br /> } catch (FileNotFoundException e) {<br /> fileInput = null;<br /> }<br /> BufferedInputStream bufferedInput = new BufferedInputStream(fileInput);<br /> return getDataSource(bufferedInput);<br /> }</p><p> /** *//**<br /> * 獲得指定InputStream的byte[]形式<br /> * @param name<br /> * @return<br /> */<br /> final static public byte[] getDataSource(InputStream is) {<br /> if (is == null) {<br /> return null;<br /> }<br /> ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();<br /> byte[] arrayByte = null;<br /> try {<br /> byte[] bytes = new byte[8192];<br /> bytes = new byte[is.available()];<br /> int read;<br /> while ((read = is.read(bytes)) >= 0) {<br /> byteArrayOutputStream.write(bytes, 0, read);<br /> }<br /> arrayByte = byteArrayOutputStream.toByteArray();<br /> } catch (IOException e) {<br /> return null;<br /> } finally {<br /> try {<br /> if (byteArrayOutputStream != null) {<br /> byteArrayOutputStream.close();<br /> byteArrayOutputStream = null;<br /> }<br /> if (is != null) {<br /> is.close();<br /> is = null;<br /> }</p><p> } catch (IOException e) {<br /> }<br /> }<br /> return arrayByte;<br /> }</p><p> /** *//**<br /> * 獲得指定JarInputStream的byte[]形式<br /> * @param jar<br /> * @return<br /> * @throws IOException<br /> */<br /> final static private byte[] getResourceData(JarInputStream jar)<br /> throws IOException {<br /> ByteArrayOutputStream data = new ByteArrayOutputStream();<br /> byte[] buffer = new byte[8192];<br /> int size;<br /> while (jar.available() > 0) {<br /> size = jar.read(buffer);<br /> if (size > 0) {<br /> data.write(buffer, 0, size);<br /> }<br /> }<br /> return data.toByteArray();<br /> }</p><p> /** *//**<br /> * 重載的getResource,檢查是否重複包含<br /> */<br /> public URL getResource(String name) {<br /> if (resources.containsKey("/" + name)) {<br /> try {<br /> return new URL("file:///" + name);<br /> } catch (MalformedURLException e) {<br /> e.printStackTrace();<br /> }<br /> }<br /> return super.getResource(name);<br /> }</p><p> /** *//**<br /> * 執行指定class類<br /> * @param clz<br /> * @param methodName<br /> * @param args<br /> */<br /> public static void callVoidMethod(Class clz, String methodName,<br /> String[] args) {<br /> Class[] arg = new Class[1];<br /> arg[0] = args.getClass();<br /> try {<br /> Method method = clz.getMethod(methodName, arg);<br /> Object[] inArg = new Object[1];<br /> inArg[0] = args;<br /> method.invoke(clz, inArg);<br /> } catch (Exception e) {<br /> System.err.println(e.getMessage());<br /> }<br /> }</p><p> /** *//**<br /> * 重載的getResourceAsStream,檢查是否重複包含<br /> */<br /> public InputStream getResourceAsStream(String name) {<br /> if (name.charAt(0) == '/') {<br /> name = name.substring(1);<br /> }<br /> if (resources.containsKey("/" + name)) {<br /> return new ByteArrayInputStream((byte[]) resources.get("/" + name));<br /> }<br /> return super.getResourceAsStream(name);<br /> }</p><p>}</p><p>
運行樣本:
package org.loon.framework.jar;</p><p>/** *//**<br /> * <p><br /> * Title: LoonFramework<br /> * </p><br /> * <p><br /> * Description:從外部啟動jar包<br /> * </p><br /> * <p><br /> * Copyright: Copyright (c) 2007<br /> * </p><br /> * <p><br /> * Company: LoonFramework<br /> * </p><br /> *<br /> * @author chenpeng<br /> * @email:ceponline@yahoo.com.cn<br /> * @version 0.1<br /> */<br />public class JarTest {</p><p> public static void main(String[] args) {<br /> //將jar包轉為byte[]<br /> byte[] resource = JarLoader.getDataSource("D:/fps_test.jar");<br /> try {<br /> //通過byte[]獲得主函數所在類<br /> Class clz = JarLoader.load(resource);<br /> //調用main函數<br /> JarLoader.callVoidMethod(clz, "main", new String[] {""});<br /> } catch (Exception e) {<br /> e.getStackTrace();<br /> }</p><p> }</p><p>}
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/cping1982/archive/2007/11/12/1880490.aspx