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

很多时候 我们写的Java程序是分模块的,有很好的扩展机制,即我们可以为我们自己的java类添加插件,来运行将来某天我们可能开发出来的类,以下称这些类为插件类。

下边是一种简单的实现方法:

Class A 作为程序的主入口,其中包含了程序的执行入口(main)函数。然后在main函数中通过外部的配置文件,然后通过外部的配置文件,我们可以获得插件类的信息(位于哪个jar包,jar包的具体路径),然后获得jar包中某一个类的实例,来完成相应的工作。这个jar包很可能是外部的jar包,是我们自己写好的,那么我们放到哪里,他才能自己找到呢?我尝试过很多次,除非将其具体目录,放到class_path中才可以成功执行,否则报的异常只有一个ClassNotFoundException,就是找不到类。不过还有一种方法,就是将该jar包解压到运行jar包所在的目录,这样就可以通过class_path中的.来获得相应的类了。不过这样会显得很不专业,java写出来的东西都是jar包啊,自己感觉的。放到claspath中,不能每次写出新的jar包都配置一遍吧!

如此出现了如下的解决办法:

想了解解决办法的含义,首先要了解java的类加载机制。众所周知,程序若想执行,必须加载到内存当中才能成功执行。java程序并不是可执行文件,由许多独立的类文件来完成。所以java中加载程序是以类为单外来完成的。这也就需要我们来简单了解一下java的class loader加载机制。

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文件的方法。

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路径)。

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

原文地址:https://www.cnblogs.com/itboys/p/11011585.html

时间: 2024-10-11 05:44:54

java中使用URLClassLoader访问外部jar包的java类的相关文章

【java基础】 如何导入外部jar包

转:from http://www.zhihu.com/question/20311561 有两种常用的方法. 1. 以外部包(External Archives)的形式导入. 在默认位于 Eclipse 窗口左侧的 Package Explorer 中,右键项目名,点击 Build Path 项下的 Add External Archives. 然后在弹出的文件选择窗口中选中 acm.jar 即可. 这种方法适用于导入单个的 jar 包. 2. 以 User Library 的形式导入. Us

java中如果删除导入的jar包,工程出现叹号解决方案

第一步:在工程上右键 第二步:选中build Path 第三步:选择Configue bulid path 第四步:选择liberary 第五步:鼠标点击带红色叉叉的 第六步:点击edit 第七步:点击apply and finish OK 搞定 原文地址:https://www.cnblogs.com/lyxcode/p/9037974.html

Intellij IDEA在maven项目中添加外部Jar包运行

一. 问题概述 我们知道Intellij IDEA是非常好用的Java语言开发的集成环境.提供了非常多实用的功能,包括了智能代码助手.代码自动提示.代码重构.各种插件等,当然也集成了maven 正常情况下,我们创建maven项目时,相关的jar包会自动导入,当然我们也可以通过IDEA右侧的maven工具来实现jar包的加载. 但我们在实际开发过程中,可能会使用外部jar包的情况,如实现与第三方公司的系统对接时,需要使用到他公司提供的jar包,这个时候在maven中央仓库找不到,该如何导入呢? 首

java项目中可能会使用到的jar包解释

一.Struts2 用的版本是struts2.3.1.1 一个简单的Struts项目所需的jar包有如下8个 1. struts2-core-2.3.1.1.jar: Struts2的核心类库. 2. xwork-core-2.3.1.1.jar: XWork核心类,XWork是一个标准的command模式实现,并且完全从web层剥离出来.WebWork被构建在Xwork上,而Struts2由Struts1和WebWork两个经典的MVC框架发展而来. 3. ognl-3.0.3.jar: 支持

eclipse生成【带有外部jar包】的java可执行jar包

之前有写过一篇使用eclipse生成java可执行jar包,但是最近的一次使用中无论如何都不成功,当双击执行打成的jar时,弹出如下错误: could not find the main class:mailTest.SendMailTest1.Program wil exit 针对这一问题,我首先想到在上一次编写java聊天小程序打包时的问题,也是这个错误,当时网上说需要更改eclipse中该项目的jdk版本为1.6,如图: 虽然意料之中,但是依旧有些遗憾的是,这样修改后生成的jar包,在双击

Java web开发中主要用到的jar包

1.Java开发中主要用到的jar包介绍:(1)java JDK基础开发包:java包和javax包.书写方式:import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; 1.java.util包:集合类list/set/map等接口和实现类.calendar,date日期操作类.properties读取配置文件类.random随机数类.Timer计时器类.

java命令行执行程序解决依赖外部jar包的问题

用java命令行直接执行程序,如果这个程序需要引用外部jar包.就不能单纯用java xx来执行 如果你的jar包和程序就在一个目录: 编译 javac -cp D:\yy\yy.jar,D\xx\xx.jar test.java 执行 java -cp D:\yy\yy.jar,D\xx\xx.jar test 但是往往我们依赖的包很多,要一个个填写估计就疯了.所有我们一般会把所有的外部依赖包都放在一个文件夹里,比如在D:\lib 编译 javac -Djava.ext.dirs=D:\lib

Eclipse中导入外部jar包步骤

昨天,学习了Jar包的打包过程,现在打算记录一下,如何在Eclipse中导入外部Jar包. 第一步:在项目中鼠标右键>>New>>点击Folder. 第二步:在弹出窗口将Folder name命名为lib,点击确定. 第三步:将Jar包拖拽或者复制到lib中. 第四步:弹出File Operation对话框,点击OK. 第五步:对导入的文件进行"解压",鼠标右键>>Build Path>>Add to Build Path 最后jar包就

Eclipse中导入外部jar包

Eclipse中导入外部jar包 方法/步骤 1 首先在项目下创建一个文件夹,保存我们的jar包. 在项目名上右击,依次点击[New]-->[Floder],打开新建文件夹窗口 步骤阅读 2 输入文件夹名称[lib],点击[ok].我们通常在lib文件夹中存放从外部引入的jar包 步骤阅读 3 找到我们要引入的jar包,鼠标选中jar包,然后按住鼠标左键不放,把jar包拖到lib文件夹中.或先复制jar包,然后在lib文件夹上右击,选择复制.此时,打开选择框,我们选择默认的[copy files