通过ClassLoader调用外部jar包

通过ClassLoader调用外部jar包

我们大家都知道,每个运行中的线程都有一个成员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>Title: LoonFramework</p>
 * <p>Description:JarLoader,用于jar包的外部操作</p>
 * <p>Copyright: Copyright (c) 2007</p>
 * <p>Company: LoonFramework</p>
 * 
@author chenpeng  
 * @email:ceponline@yahoo.com.cn 
 * 
@version 0.1
 
*/

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;

public class JarLoader extends ClassLoader ...{
    
//资源缓存
    public static Hashtable resources = new Hashtable();
 
    
public static JarLoader loader = new JarLoader();

    
public static Class load(byte[] resource) throws Exception ...{
        
// 主函数所在类全称
        String mainClassName = "";
        
//class资源及实体缓存
        ArrayList classNames = new ArrayList();
        ArrayList classBuffers 
= new ArrayList();
        
// 存储依赖类
        HashMap depends = new HashMap();
        
// 将byte[]转为JarInputStream
        JarInputStream jar = new JarInputStream(new ByteArrayInputStream(
                resource));
        Manifest manifest 
= jar.getManifest();
        
// 当Main-Class被声明时,获得主函数所在类全称
        if (manifest != null...{
            mainClassName 
= manifest.getMainAttributes().getValue("Main-Class");
        }

        
// 依次获得对应JAR文件中封装的各个被压缩文件的JarEntry
        JarEntry entry;
        
while ((entry = jar.getNextJarEntry()) != null...{
            
// 当找到的entry为class时
            if (entry.getName().toLowerCase().endsWith(".class")) ...{
                
// 将类路径转变为类全称
                String name = entry.getName().substring(0,
                        entry.getName().length() 
- ".class".length()).replace(
                        
'/''.');
                
// 加载该类
                byte[] data = getResourceData(jar);
                
// 缓存类名及数据
                classNames.add(name);
                classBuffers.add(data);

            }
 else ...{
                
// 非class结尾但开头字符为'/'时
                if (entry.getName().charAt(0== '/'...{
                    resources.put(entry.getName(), getResourceData(jar));
                
// 否则追加'/'后缓存    
                }
 else ...{
                    resources.put(
"/" + entry.getName(), getResourceData(jar));
                }

            }

        }

        
//当获得的main-class名不为空时
        while (classNames.size() > 0...{
            
//获得类路径全长
            int n = classNames.size();
            
for (int i = classNames.size() - 1; i >= 0; i--...{
                
try ...{
                    
//查询指定类
                    loader.defineClass((String) classNames.get(i),
                            (
byte[]) classBuffers.get(i), 0,
                            ((
byte[]) classBuffers.get(i)).length);
                    
//获得类名
                    String pkName = (String) classNames.get(i);
                    
if (pkName.lastIndexOf('.'>= 0...{
                        pkName 
= pkName
                                .substring(
0, pkName.lastIndexOf('.'));
                        
if (loader.getPackage(pkName) == null...{
                            loader.definePackage(pkName, 
nullnullnull,
                                    
nullnullnullnull);
                        }

                    }

                    
//查询后删除缓冲
                    classNames.remove(i);
                    classBuffers.remove(i);
                }
 catch (NoClassDefFoundError e) ...{
                    depends.put((String) classNames.get(i), e.getMessage()
                            .replaceAll(
"/""."));
                }
 catch (UnsupportedClassVersionError e) ...{
                    
//jre版本错误提示
                    throw new UnsupportedClassVersionError(classNames.get(i)
                            
+ "" + System.getProperty("java.vm.name"+ " "
                            
+ System.getProperty("java.vm.version"+ ")");
                }

            }

            
if (n == classNames.size()) ...{
                
for (int i = 0; i < classNames.size(); i++...{
                    System.err.println(
"NoClassDefFoundError:"
                            
+ classNames.get(i));
                    String className 
= (String) classNames.get(i);
                    
while (depends.containsKey(className)) ...{
                        className 
= (String) depends.get(className);
                    }

                }

                
break;
            }

        }

        
try ...{
            
//加载
            Thread.currentThread().setContextClassLoader(loader);
            
// 获得指定类,查找其他类方式相仿
            return Class.forName(mainClassName, true, loader);
        }
 catch (ClassNotFoundException e) ...{
            String className 
= mainClassName;
            
while (depends.containsKey(className)) ...{
                className 
= (String) depends.get(className);
            }

            
throw new ClassNotFoundException(className);
        }

    }


    
/** *//**
     * 获得指定路径文件的byte[]形式
     * 
@param name
     * 
@return
     
*/

    
final static public byte[] getDataSource(String name) ...{
        FileInputStream fileInput;
        
try ...{
            fileInput 
= new FileInputStream(new File(name));
        }
 catch (FileNotFoundException e) ...{
            fileInput 
= null;
        }

        BufferedInputStream bufferedInput 
= new BufferedInputStream(fileInput);
        
return getDataSource(bufferedInput);
    }

    
    
/** *//**
     * 获得指定InputStream的byte[]形式
     * 
@param name
     * 
@return
     
*/

    
final static public byte[] getDataSource(InputStream is) ...{
        
if (is == null...{
            
return null;
        }

        ByteArrayOutputStream byteArrayOutputStream 
= new ByteArrayOutputStream();
        
byte[] arrayByte = null;
        
try ...{
            
byte[] bytes = new byte[8192];
            bytes 
= new byte[is.available()];
            
int read;
            
while ((read = is.read(bytes)) >= 0...{
                byteArrayOutputStream.write(bytes, 
0, read);
            }

            arrayByte 
= byteArrayOutputStream.toByteArray();
        }
 catch (IOException e) ...{
            
return null;
        }
 finally ...{
            
try ...{
                
if (byteArrayOutputStream != null...{
                    byteArrayOutputStream.close();
                    byteArrayOutputStream 
= null;
                }

                
if (is != null...{
                    is.close();
                    is 
= null;
                }


            }
 catch (IOException e) ...{
            }

        }

        
return arrayByte;
    }


    
/** *//**
     * 获得指定JarInputStream的byte[]形式
     * 
@param jar
     * 
@return
     * 
@throws IOException
     
*/

     
final static private byte[] getResourceData(JarInputStream jar)
            
throws IOException ...{
        ByteArrayOutputStream data 
= new ByteArrayOutputStream();
        
byte[] buffer = new byte[8192];
        
int size;
        
while (jar.available() > 0...{
            size 
= jar.read(buffer);
            
if (size > 0...{
                data.write(buffer, 
0, size);
            }

        }

        
return data.toByteArray();
    }


     
/** *//**
      * 重载的getResource,检查是否重复包含
      
*/

    
public URL getResource(String name) ...{
        
if (resources.containsKey("/" + name)) ...{
            
try ...{
                
return new URL("file:///" + name);
            }
 catch (MalformedURLException e) ...{
                e.printStackTrace();
            }

        }

        
return super.getResource(name);
    }


    
/** *//**
     * 执行指定class类
     * 
@param clz
     * 
@param methodName
     * 
@param args
     
*/

    
public static void callVoidMethod(Class clz, String methodName,
            String[] args) 
...{
        Class[] arg 
= new Class[1];
        arg[
0= args.getClass();
        
try ...{
            Method method 
= clz.getMethod(methodName, arg);
            Object[] inArg 
= new Object[1];
            inArg[
0= args;
            method.invoke(clz, inArg);
        }
 catch (Exception e) ...{
            System.err.println(e.getMessage());
        }

    }

    
     
/** *//**
      * 重载的getResourceAsStream,检查是否重复包含
      
*/

    
public InputStream getResourceAsStream(String name) ...{
        
if (name.charAt(0== '/'...{
            name 
= name.substring(1);
        }

        
if (resources.containsKey("/" + name)) ...{
            
return new ByteArrayInputStream((byte[]) resources.get("/" + name));
        }

        
return super.getResourceAsStream(name);
    }


}


运行示例:

package org.loon.framework.jar;


/** *//**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:从外部启动jar包
 * </p>
 * <p>
 * Copyright: Copyright (c) 2007
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 * 
 * 
@author chenpeng
 * @email:ceponline@yahoo.com.cn
 * 
@version 0.1
 
*/

public class JarTest ...{

    
public static void main(String[] args) ...{
        
//将jar包转为byte[]
        byte[] resource = JarLoader.getDataSource("D:/fps_test.jar");
        
try ...{
            
//通过byte[]获得主函数所在类
            Class clz = JarLoader.load(resource);
            
//调用main函数
            JarLoader.callVoidMethod(clz, "main"new String[] ...{""});
        }
 catch (Exception e) ...{
            e.getStackTrace();
        }


    }


}


这时即使指定jar包没有被我们添加到lib中,程序依旧被顺利启动了。

但是有个缺点,在没有优化的前提下,这种直接加载外部包的速度在jvm会有很大损耗。

我们可以看到,fps值降低到正常值的50%左右(此fps实例见我CSDN以前的文章),所以并不适合直接运用在需要复杂运算的jar中类调用上(当然,有兴趣的话,可以在代码中优化,我自己在项目中写的另一个例子速度明显比这个快)。但是对于运算量小的外部jar调用,还是很方便的。
posted on 2007-11-12 15:38 cping 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/cping1982/archive/2007/11/12/2258094.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/262983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git回滚操作的总结

git结构和各操作之间的关系 1&#xff0c;撤销add操作&#xff1a; git reset 2&#xff0c;撤销commit操作&#xff1a; git reset –soft 保留源码&#xff0c;只回退commit信息到某个版本&#xff0c;不涉及index的回退&#xff0c;如果还需要提交&#xff0c;直接commit即…

Blender建模与游戏换装(转载文)

本文转载自https://my.oschina.net/huliqing/blog/880113?hmsrtoutiao.io 如果本文涉及侵权行为&#xff0c;请原作者联系博主邮箱&#xff0c;我将及时进行删除处理 博主邮箱&#xff1a;yibiandaoaliyun.com 前言 本文将详细讲解3D游戏中换装的原理及换装中的一些重点问题&a…

出路在哪里?出路在于思路!智者无敌

有人工作&#xff0c;有人继续上学&#xff0c;大家千万不要错过这篇文章&#xff0c;能看到这篇文章也是一种幸运&#xff0c;真的受益匪浅&#xff0c;对我有很大启迪&#xff0c;这篇文章将会改变我的一生&#xff0c;真的太好了&#xff0c;希望与有缘人分享&#xff0c;也…

xml02 XML编程(CRUD)增删查改

XML解析技术概述 Demo2.java import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; public class Demo2 { public static void main(String args[])throws Exception { //1.创建工程 DocumentBuilderFac…

ASP.NET Web Game 架构设计1--服务器基本结构

ASP.NET Web Game 架构设计1--服务器基本结构 1. 基本结构图 2. 系统组成与角色 整个系统大体上分为三个部分&#xff1a;1.网页客户端。2.IIS Web服务器。3.数据库及逻辑服务器。其中Web服务器不处理任何逻辑&#xff0c;它的作用只有两点&#xff1a;1.承载用户。…

人人网 Windows Phone 7 应用开发起步

目前&#xff0c;人人网在国内高校学生中的普及率非常高。前段时间&#xff0c;大概是11月下旬的样子&#xff0c;人人网发布了Windows Phone 7客户端的公测版。我想&#xff0c;Windows Phone 7本地化的优劣&#xff0c;直接关系到其将来在国内的市场份额。而诸如人人等针对学…

XP Sp2下双机通过无线网卡实现Internet共享

两台均有无线网卡、装有XP SP2系统的计算机如何实现共享Internet上网呢&#xff0c;请参考一下步骤&#xff1a; 1、打开两台计算机的无线网络连接属性&#xff0c;并切换至“无线网络配置”页签。2、点中右下角的高级按钮设置&#xff0c;选中最下面的“仅计算机到计算机” 和…

C#面向对象设计模式第九讲:Composite 组合模式(结构型模式)

&#xff08;根据MSDN Webcast相关课程整理&#xff09; 由俄罗斯套娃讲起。娃娃里又包含另一个娃娃&#xff0c;最后那个不包含任何娃娃。 组合模式&#xff0c;采用树型结构来实现普遍存在的对象容器&#xff0c;将本原一对多的复杂的关系&#xff0c;转换成一对一的简单关系…

Docker for Windows

安装条件&#xff1a;必须是 Win10 Pro 或者 Enterprise version. 转载于:https://www.cnblogs.com/qijiage/p/9261258.html

《火影忍者:究级风暴》渲染技术究极解析!

http://www.opengpu.org/forum.php?modviewthread&tid6609 与Takara Tomy公司的《火影忍者》系列游戏不同&#xff0c;初次登陆PS3平台的本作是由日本CyberConnect2制作的对战格斗游戏《火影忍者&#xff1a;终极英雄》系列的最新作。虽然游戏的开发商仍然是CyberConnect2…

工程中新增Properties

如一开始工程中是没有Properties文件夹的&#xff01; 但工程目录文件夹下却有一个Properties&#xff1a; 现在要向这个Properties文件夹中添资源文件。操作步骤&#xff1a; [添加新项]->[资源文件] 再将Resource.resx文件拉到Properties DONE!!!

Django:序列化的几种方法

前言 关于序列化操作&#xff0c;就是将一个可迭代的数据结构&#xff0c;通过便利的方式进行我们所需要的操作。 今天历来归纳一下&#xff0c;Django中的几种不同得分方法&#xff0c;已经Django-restframework提供的方法 创建一个Django的项目 再新建一个app 创建一个模型&a…

c#通过app.manifest使程序 右键 以管理员身份运行

c#通过app.manifest使程序以管理员身份运行 时间:2013-06-27 22:47来源:网络收集本站整理 作者:jtydl 点击: 1175 次微软在Windows Vista开始引入了UAC&#xff08;用户帐户控制&#xff09;新技术&#xff08;点击这儿了解什么是UAC&#xff09;。当程序执行时需要权限的话&am…

25款操作系统全面接触 [2]

Sun Solaris Sun Microsystems公司早期的操作系统版本Sun OS是基于BSD的。在1993年&#xff0c;他们与AT&T合作&#xff0c;转向了UNIX System V&#xff0c;并发布了称作Solaris.System V release 4的系统&#xff0c;这是一个UNIX System V和BSD的整合体。Solaris系统主…

Nuget发布Dll

今天要开始写ViewModel了&#xff0c;写完之后系统里的ViewModel都汇总到我这里&#xff0c;然后由我负责ViewModel的发布跟维护&#xff0c;所以Nuget发布Dll就要熟练啦~ 一&#xff0c;安装工具 1&#xff0c;Nuget Package Manager 2,NuGet.exe 下载地址为&#xff1a;http:…

技巧/诀窍:在ASP.NET中重写URL(转)

技巧/诀窍&#xff1a;在ASP.NET中重写URL 【原文地址】Tip/Trick: Url Rewriting with ASP.NET 【原文发表日期】 Monday, February 26, 2007 9:27 PM 经常有人请我指导应该如何动态地“重写”URL&#xff0c;以在他们的ASP.NETweb应用中发布比较干净的URL端点。这个博客帖…

妙趣横生算法 3:寻找相同元素的指针

实例说明 在已知两个从小到大的有序的数表中寻找出现的相同元素在第一个数表中的指针。 运行结果 实例解析 设两个数表的首元素指针分别为pa和pb,两个数表分别有元素an和bn个。另外&#xff0c;引入两个指针变量ca和cb,分别指向两个数表的当前访问元素。由于两个数表从小到大有…

Nginx 笔记与总结(14)expires 缓存设置

设置缓存&#xff0c;可以提高网站性能。 当网站的部分内容&#xff0c;比如新闻站的图片&#xff0c;一旦发布就不太可能发生更改&#xff0c;此时需要用户在访问一次页面之后&#xff0c;把该页面的图片缓存在用户的浏览器端一段时间&#xff0c;就可以用到 nginx 的 expires…

WP7应用开发笔记(8) IP输入框控件

因为需要在手机上配置IP&#xff0c;我需要一个界面输入IP地址&#xff0c;虽然直接使用TextBox&#xff0c;但是这样不太友好&#xff0c;我希望能够有和Windows网络设置上一样的IP输入框。所以决定写一个自定义控件。 设计控件外观 4个TextBox和3个显示“.”的TextBlock就可以…

C#并发实战Parallel.ForEach使用

前言&#xff1a;最近给客户开发一个伙食费计算系统&#xff0c;大概需要计算2000个人的伙食。需求是按照员工的预定报餐计划对消费记录进行检查&#xff0c;如有未报餐有刷卡或者有报餐没刷卡的要进行一定的金额扣减等一系列规则。一开始我的想法比较简单&#xff0c;直接用一…