java中使用URLClassLoader访问外部jar包的java类

不少时候 咱们写的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();  
 }  
}  

 


以上的这种状况能够在别的project项目里写test方法,是平时最经常使用的,若是当.class文件里有依赖别的jar包里的对象的时候,就要把该jar包拷贝到写此测试方法的project并buildPath,否则的话运行的时候会报找不到Class对象的异常。测试

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;  
    
 }  

 


下面上第二段代码

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();  
    }  
}  
相关文章
相关标签/搜索