不少時候 咱們寫的Java程序是分模塊的,有很好的擴展機制,即咱們能夠爲咱們本身的java類添加插件,來運行未來某天咱們可能開發出來的類,如下稱這些類爲插件類。java
下邊是一種簡單的實現方法:c++
Class A 做爲程序的主入口,其中包含了程序的執行入口(main)函數。而後在main函數中經過外部的配置文件,而後經過外部的配置文件,咱們能夠得到插件類的信息(位於哪一個jar包,jar包的具體路徑),而後得到jar包中某一個類的實例,來完成相應的工做。這個jar包極可能是外部的jar包,是咱們本身寫好的,那麼咱們放到哪裏,他才能本身找到呢?我嘗試過不少次,除非將其具體目錄,放到class_path中才能夠成功執行,不然報的異常只有一個ClassNotFoundException,就是找不到類。不過還有一種方法,就是將該jar包解壓到運行jar包所在的目錄,這樣就能夠經過class_path中的.來得到相應的類了。不過這樣會顯得很不專業,java寫出來的東西都是jar包啊,本身感受的。放到claspath中,不能每次寫出新的jar包都配置一遍吧!bootstrap
如此出現了以下的解決辦法:dom
想了解解決辦法的含義,首先要了解java的類加載機制。衆所周知,程序若想執行,必須加載到內存當中才能成功執行。java程序並非可執行文件,由許多獨立的類文件來完成。因此java中加載程序是以類爲單外來完成的。這也就須要咱們來簡單瞭解一下java的class loader加載機制。ide
java程序開始執行,遇到的第一個classloader是bootstrap classloader,這個classloader是用c++語言編寫,經過他來完成加載java中的核心類。第二個classloader是extension classloader,加載的是jre/lib目錄中的ext目錄中的jar包。而後第三個是system classloader,也被稱爲應用加載器,主要負責完成加載-classpath 或者系統中的全局變量ClassPath中的類。System.out.println(System.getProperty(「java.class.path」));能夠得到classpath的配置,也就是system classloader 加載的類,第四個class loader多是用戶自定義的加載器,來自定義加載類。一般一個類的加載過程是這樣的經過當前的類加載器的父加載器嘗試查找,若是沒有再找其父加載器嘗試加載,直到最終的bootstrap classloader爲止,若是尚未找到,那麼就開始從上往下加載類。這樣作的目的是防止自定義的類來覆蓋系統中的類,若是沒有這種機制很容易出現這種笑話,本身寫了一個String類,而後new string的時候是本身寫的String類,這樣就比較好玩了。函數
1.本身定義URLClassLoader對象加載外部jar包,針對jar包裏面再也不出現別的jar包的狀況,即只解析.class文件:測試
private static void test1() { String path = "D:\\test.jar";//外部jar包的路徑
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();//全部的Class對象
Map<Class<?>, Annotation[]> classAnnotationMap = new HashMap<Class<?>, Annotation[]>();//每一個Class對象上的註釋對象
Map<Class<?>, Map<Method, Annotation[]>> classMethodAnnoMap = new HashMap<Class<?>, Map<Method,Annotation[]>>();//每一個Class對象中每一個方法上的註釋對象
try { JarFile jarFile = new JarFile(new File(path)); URL url = new URL("file:" + path); ClassLoader loader = new URLClassLoader(new URL[]{url});//本身定義的classLoader類,把外部路徑也加到load路徑裏,使系統去該路經load對象
Enumeration<JarEntry> es = jarFile.entries(); while (es.hasMoreElements()) { JarEntry jarEntry = (JarEntry) es.nextElement(); String name = jarEntry.getName(); if(name != null && name.endsWith(".class")){//只解析了.class文件,沒有解析裏面的jar包 //默認去系統已經定義的路徑查找對象,針對外部jar包不能用 //Class<?> c = Thread.currentThread().getContextClassLoader().loadClass(name.replace("/", ".").substring(0,name.length() - 6));
Class<?> c = loader.loadClass(name.replace("/", ".").substring(0,name.length() - 6));//本身定義的loader路徑能夠找到
System.out.println(c); classes.add(c); Annotation[] classAnnos = c.getDeclaredAnnotations(); classAnnotationMap.put(c, classAnnos); Method[] classMethods = c.getDeclaredMethods(); Map<Method, Annotation[]> methodAnnoMap = new HashMap<Method, Annotation[]>(); for(int i = 0;i<classMethods.length;i++){ Annotation[] a = classMethods[i].getDeclaredAnnotations(); methodAnnoMap.put(classMethods[i], a); } classMethodAnnoMap.put(c, methodAnnoMap); } } System.out.println(classes.size()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
2.第二種狀況是針對加載jar包裏面的jar包的Class對象,還有讀取某一個properties文件的方法。ui
private static void test2() { String path = "D:\\test.jar";//此jar包裏還有別的jar包
try { JarFile jarfile = new JarFile(new File(path)); Enumeration<JarEntry> es = jarfile.entries(); while (es.hasMoreElements()) { JarEntry je = es.nextElement(); String name = je.getName(); if(name.endsWith(".jar")){//讀取jar包裏的jar包
File f = new File(name); JarFile j = new JarFile(f); Enumeration<JarEntry> e = j.entries(); while (e.hasMoreElements()) { JarEntry jarEntry = (JarEntry) e.nextElement(); System.out.println(jarEntry.getName()); //.........接下去和上面的方法相似
} } // System.out.println(je.getName());
if(je.getName().equals("entity_pk.properties")){ InputStream inputStream = jarfile.getInputStream(je); Properties properties = new Properties(); properties.load(inputStream); Iterator<Object> ite = properties.keySet().iterator(); while (ite.hasNext()) { Object key = ite.next(); System.out.println(key + " : " +properties.get(key)); } } } } catch (IOException e) { e.printStackTrace(); } }
3.第三種狀況是在該項目下獲取某個包的Class對象,固然了,測試方法是在該項目下寫的(這樣classLoader就直接能夠知道對象了,不須要再自定義URLClassLoader了,用Thread.currentThread().getContextClassLoader().loadClass(.....)就能夠直接得到Class對象了,回去ClassPath下找,System.out.print(System.getProperty("java.class.path"))就能夠找到classPath路徑)。url
private static Set<Class<?>> getclass() { Set<Class<?>> classes = new LinkedHashSet<Class<?>>(); boolean flag = true;//是否循環迭代
String packName = "com.yk.framework.db"; // String packName = "org.jdom";
String packDir = packName.replace(".", "/"); Enumeration<URL> dir; try { dir = Thread.currentThread().getContextClassLoader().getResources(packDir); while(dir.hasMoreElements()){ URL url = dir.nextElement(); System.out.println("url:***" + url); String protocol = url.getProtocol();//得到協議號
if("file".equals(protocol)){ System.err.println("file類型的掃描"); String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); System.out.println("filePath :" + filePath); findAndAddClassesInPackageByFile(packName, filePath,flag,classes); }else if("jar".equals(protocol)){ System.err.println("jar類型掃描"); JarFile jar; jar = ((JarURLConnection)url.openConnection()).getJarFile(); Enumeration<JarEntry> entries = jar.entries(); while(entries.hasMoreElements()){ JarEntry entry = entries.nextElement(); String name = entry.getName(); System.out.println(">>>>:" + name); //......
} } } } catch (IOException e) { e.printStackTrace(); } System.out.println(classes.size()); return classes; }
下面上第二段代碼spa
private static void findAndAddClassesInPackageByFile(String packName, String filePath, final boolean flag, Set<Class<?>> classes) { File dir = new File(filePath); if( !dir.exists() || !dir.isDirectory()){ System.out.println("此路徑下沒有文件"); return; } File[] dirfiles = dir.listFiles(new FileFilter(){ @Override public boolean accept(File pathname) { return flag && pathname.isDirectory() || pathname.getName().endsWith(".class"); } }); for (File file : dirfiles) { if(file.isDirectory()){//若是是目錄,繼續掃描
findAndAddClassesInPackageByFile(packName + "." + file.getName(),file.getAbsolutePath(),flag,classes); }else{//若是是文件
String className = file.getName().substring(0,file.getName().length() - 6); System.out.println("類名:" +className); try { classes.add(Thread.currentThread().getContextClassLoader().loadClass(packName + "." + className)); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } }
補充:
4.讀取jar包中的entity_pk.properties鍵值對到項目本地entity_pk.properties文件中
/** * 讀取jar包中的entity_pk.properties鍵值對到項目本地entity_pk.properties文件中 */
private static void test2() { String path = "D:\\test.jar"; try { JarFile jarFile = new JarFile(new File(path)); Enumeration<JarEntry> es = jarFile.entries(); while (es.hasMoreElements()) { JarEntry jarEntry = (JarEntry) es.nextElement(); String name = jarEntry.getName(); if(name.equals("entity_pk.properties")){ InputStream inputStream = new FileInputStream(name); Properties prop = new Properties(); prop.load(inputStream);//先load本地的entity_pk.propertoes文件
InputStream inputStream2 = jarFile.getInputStream(jarEntry);//得到jar包entity_pk.propertoes文件流
Properties prop2 = new Properties(); prop2.load(inputStream2); Enumeration<Object> ks = prop2.keys(); OutputStream out = new FileOutputStream(name); while (ks.hasMoreElements()) { String key = (String)ks.nextElement(); System.out.println(key + " : " +prop2.getProperty(key)); prop.setProperty(key, prop2.getProperty(key));//把jar包entity_pk.properties鍵值對放入本地
} prop.store(out,""); } } } catch (IOException e) { e.printStackTrace(); } }
5.讀寫db_config.xml文件
/** * 讀寫db_config.xml文件 */
private static void test3() { String path = "D:\\test.jar"; try { JarFile jarFile = new JarFile(new File(path)); Enumeration<JarEntry> es = jarFile.entries(); while (es.hasMoreElements()) { JarEntry jarEntry = (JarEntry) es.nextElement(); String name = jarEntry.getName(); if(name.equals("db_config.xml")){ InputStream inputStream = jarFile.getInputStream(jarEntry); SAXBuilder builder = new SAXBuilder(); Document doc = null; try { doc = builder.build(inputStream); if(doc != null){ Element e = doc.getRootElement(); Element proxool = e.getChild("proxool"); List<Element> children = proxool.getChildren(); for (Element element : children) { System.out.println(element.getText()); } Element s = new Element("test"); s.addContent("test"); proxool.addContent(s); XMLOutputter outputter = new XMLOutputter(); outputter.output(doc, new FileOutputStream("config/db_config.xml")); } } catch (JDOMException e) { e.printStackTrace(); }finally{ if(inputStream != null){ inputStream.close(); } } } } } catch (IOException e) { e.printStackTrace(); } }