`
webcode
  • 浏览: 6111240 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

向真正Java高手请教ant构建工具的类装载器问题

阅读更多

问题概述:每个ClassLoader本身只能分别加载特定位置和目录中的类,但是,ClassLoader被设计成了一种委托模式,使得某一个ClassLoader可以委托它的父级类装载器去加载类,从而让应用程序可以借助某一个子级的ClassLoader去多个位置和目录中进行类的加载。这就好比“儿子”除了可以花自己的钱,他还可以花“父亲”的钱,“父亲”又可以花“父亲的父亲”的钱,所以,最终能通过“儿子”花出去的钱包括他历代前辈的钱。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。
现在的问题是,我编写了一个类装载器去加载特定目录中的类,使用java.exe测试这个类加载器时,测试结果完全正常,可以看到委托效果。而我使用ant工具去调用测试程序时,结果就有点问题了,我编写的类装载器似乎并没有委托其父级类加载器去加载类,而总是自己加载。由于本人才学疏浅,且实在没有精力去研究ant工具的源码,无法了解其类加载内部细节,现在特针对这个问题,向真正的java高手们请教。为了便于高手们快速了解我的问题所在,也便于一些中手们学习,我写出了详细的实验步骤,对于java新手,建议不要参与讨论了,免得我耽误了您宝贵时间。

1.源程序:MainClass.java
package cn.itcast;
public class MainClass
{
public static void main(String [] args)
{
ClassLoader loader = MainClass.class.getClassLoader();
//打印出当前的类装载器,及该类装载器的各级父类装载器
while(loader != null)
{
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
//加载AuxiliaryClass类
System.out.println(AuxiliaryClass.class.getName());
}

}
源程序:AuxiliaryClass.java
package cn.itcast;
public class AuxiliaryClass
{
}

2.源文件及build结果文件的目录结构
f:\project
|__src
| |__cn
| |__itcast
| |__MainClass.java
| |__AuxiliaryClass.java
|__build.xml
|__classes
|__cn
|__itcast
|__MainClass.class
|__AuxiliaryClass.class
3.build.xml文件内容
<project name="antloader" default="run">
<property name="classes.dir" value="classes" />
<property name="src.dir" value="src" />

<target name="init">
<mkdir dir="${classes.dir}" />
</target>

<target name="compile" depends="init">
<javac destdir="${classes.dir}" >
<src path="${src.dir}" />
</javac>
</target>

<target name="run" depends="compile">
<java classname="cn.itcast.MyClassLoader">
<classpath>
<pathelement location="${classes.dir}"/>
</classpath>
</java>
</target>
</project>
4.进入project目录中运行ant,执行结果正常,如下:
org.apache.tools.ant.loader.AntClassLoader2
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
cn.itcast.AuxiliaryClass
5.修改build.xml文件,将最后名称为“run”的target(执行目标)修改成如下形式,即不设置其中的<classpath>子元素。
<target name="run" depends="compile">
<java classname="cn.itcast.MyClassLoader">
<!--classpath>
<pathelement location="${classes.dir}"/>
</classpath-->
</java>
</target>
再次执行ant,将报告如下错误信息
Could not find cn.itcast.MainClass. Make sure you have it in your classpath
at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:170)
在执行ant的命令行窗口中设置classpath环境变量:
set CLASSPATH=f:\project\classes;
再次执行ant,执行结果正常,如下:
java.net.URLClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
cn.itcast.AuxiliaryClass
这个实验说明CLASSPATH环境变量对ant起了作用,并且在这种情况下,类的加载入器不再是org.apache.tools.ant.loader.AntClassLoader2,而是java.net.URLClassLoader。
6.修改build.xml文件,让ant生成的AuxiliaryClass.class文件与MainClass文件位于不同的目录中,即结果目录如下:
f:\project
|__src
||__cn
| |__itcast
| |__MainClass.java
| |__AuxiliaryClass.java
|__build.xml
|__classes
| |__cn
| |__itcast
| |__MainClass.class
|__cn
|__itcast
|__AuxiliaryClass.class
修改后的build.xml文件内容如下:
<project name="antloader" default="run">
<property name="classes.dir" value="classes" />
<property name="src.dir" value="src" />
<property name="mainclass" value="cn.itcast.MainClass" />

<target name="init">
<mkdir dir="${classes.dir}" />
</target>

<target name="compile" depends="init">
<javac destdir="${classes.dir}" >
<src path="${src.dir}" />
<include name="cn/itcast/MainClass.java" />
</javac>
<delete file="${classes.dir}/cn/itcast/AuxiliaryClass.class" />
<javac destdir="." >
<src path="${src.dir}" />
<include name="cn/itcast/AuxiliaryClass.java" />
</javac>
</target>

<target name="run" depends="clean,compile">
<java classname="${mainclass}">
<!--classpath>
<pathelement location="${classes.dir}"/>
</classpath-->
<arg line="${arg0} ${arg1}" />
</java>
</target>

<target name="clean">
<delete dir="${classes.dir}" />
</target>

</project>
因为第一个javac任务编译MainClass.java时,也会编译它引用的AuxiliaryClass.java文件,所以,增加了delete任务删除掉生成的AuxiliaryClass.class文件,然后再使用一个javac任务将AuxiliaryClass.java编译到另外一个目录中。java任务中也增加了一个<arg>子元素,用于为java虚拟机传递参数,在这一步暂时不需要这个元素,在下一步的实验中将使用这个元素。
再次执行ant,将报告如下错误信息
Could not find cn.itcast.Auxiliary. Make sure you have it in your classpath
at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:170)
在执行ant的命令行窗口中设置classpath环境变量,将编译后生成的AuxiliaryClass.class类所在的目录也加入进CLASSPATH环境变量中:
set CLASSPATH=f:\project\classes;f:\project;
再次执行ant,执行结果正常,如下:
java.net.URLClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
cn.itcast.AuxiliaryClass
这个实验再次说明CLASSPATH环境变量对ant起了作用,将AuxiliaryClass.class放在了classpath环境变量指定的另外一个目录中,也可以被ant工具的java任务装载。
7.修改MainClass.java文件,让其扩展成一个类装载器,专门负责从一个特定的目录中去加载类。MainClass同时也作为一个启动运行类,在其main方法中通过MainClass这个类装载器加载AuxiliaryClass类。
源程序:MainClass.java
package cn.itcast;
import java.io.*;

public class MainClass extends ClassLoader
{
private String path = null;
public MainClass(String path)
{
//错误检查省略
this.path = path;
}

protected Class findClass(String name) throws ClassNotFoundException
{
try
{
File f = new File(path,name.substring(name.lastIndexOf('.')+1) + ".class");
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int b = 0;
while((b=fis.read()) != -1)
{
bos.write(b);
}
byte [] buf = bos.toByteArray();
fis.close();
bos.close();
return defineClass(name,buf,0,buf.length);
}catch(Exception e)
{
throw new ClassNotFoundException(name + "is not found!");
}
}

public static void main(String [] args) throws Exception
{
Class cls = new MainClass(args[1]).loadClass(args[0]);
ClassLoader loader = cls.getClassLoader();
//打印出的动态加载的AuxiliaryClass的类装载器,及该类装载器的各级父类装载器
while(loader != null)
{
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
}

}
按如下方式执行ant命令,其中第一个参数为要加载的类,第二个参数为到哪个目录中去加载如类。
ant -Darg0=cn.itcast.AuxiliaryClass -Darg1=cn\itcast
命令执行的结果为:
cn.itcast.MainClass
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
从第一行打印的内容上可以看到:AuxiliaryClass类的类装载器为MainClass。这个结果与我的预期不同,因为按照类加载器的委托机制,MailClass类加载器将先委托其父级类装载器AppClassLoader加载AuxiliaryClass,而AuxiliaryClass所在的目录f:\project已经在第6步中加入到了Classpath环境变量当中,AppClassLoader可以成功加载AuxiliaryClass,所以,第一行打印出来的类装载器应该是AppClassLoader。为了印证我的想法,我改用java.exe来执行上面的程序:
java cn.itcast.MainClass cn.itcast.AuxiliaryClass cn\itcast
执行结果如下:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
可见,使用java.exe执行上面的程序时,AuxiliaryClass类的类装载器确实是MailClass类加载器的父级类加载器AppClassLoader。这就是我这次问题的内容:为什么在ant环境下运行,MailClass类加载器没有委托其父级类装载器AppClassLoader加载AuxiliaryClass类,而是自己加载了呢?就这个问题,本人向真正的Java高手们请教?请您帮忙解释一下原因。
8.为了印证类加载器的委托机制,我们重新设置CLASSPATH环境变量,该环境变量不再包含AuxiliaryClass所在的目录f:\project。
set CLASSPATH=f:\project\classes;
用cd命令进入f:\目录(避免当前目录的干扰),接着重复执行如下的java命令:
java cn.itcast.MainClass cn.itcast.AuxiliaryClass project\cn\itcast
执行结果如下:
cn.itcast.MainClass
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
可见,这次AuxiliaryClass类的类装载器是MailClass类,这是因为MailClass类装载器的父级类加载器AppClassLoader找不到AuxiliaryClass类,加载过程又退回到MailClass类装载器,MailClass类装载器从project\cn\itcast目录中成功找到AuxiliaryClass类,所以,这次打印出的类装载器为MailClass。

分享到:
评论

相关推荐

    模具状态监测行业发展趋势:预计到2030年市场规模为5.06亿美元

    模具状态监测市场:6.8%的年复合增长率引领制造业智能化升级 在快速发展的制造业中,模具作为生产过程中的核心部件,其状态直接影响到产品的质量和生产效率。然而,模具的损耗和故障往往难以预测,给企业带来不小的损失。如今,随着模具状态监测技术的兴起,这一切正在发生改变。这项创新技术不仅能够帮助企业提前发现模具的潜在问题,还能显著延长模具的使用寿命,提升生产效率。但你真的了解这个市场的潜力和现状吗?让我们一同揭开模具状态监测市场的神秘面纱。 市场概况: 根据QYR(恒州博智)的统计,2023年全球模具状态监测市场的销售额已经达到了3.2亿美元,预计到2030年,这一数字将攀升至5.06亿美元,年复合增长率高达6.8%。这一显著的增长背后,是制造业对智能化、自动化生产需求的不断提升,以及模具状态监测技术在提高生产效率、降低维护成本方面的显著优势。 技术创新与趋势: 模具状态监测技术主要依赖于传感器、数据分析和处理等技术手段,能够实时采集模具的温度、振动、压力等指标,并通过与预设参数的比对,及时识别模具的异常情况。随着物联网、大数据和人工智能等技术的不断发展,模具状态监测技术将更加智能化,能够提供

    Kubernetes DevOps实践工作坊-从理论到实战操作脚本集(含源码).zip

    Kubernetes DevOps实践工作坊-从理论到实战操作脚本集(含源码).zip [资源说明] 1、该项目是团队成员近期最新开发,代码完整,资料齐全,含设计文档等 2、上传的项目源码经过严格测试,功能完善且能正常运行,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的高校学生、教师、科研工作者、行业从业者下载使用,可借鉴学习,也可直接作为毕业设计、课程设计、作业、项目初期立项演示等,也适合小白学习进阶,遇到问题不懂就问,欢迎交流。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 5、不懂配置和运行,可远程教学 欢迎下载,学习使用!

    基于springboot+vue3+uniapp的点餐小程序源代码+数据库+文档说明(高分毕设)

    基于springboot+vue3+uniapp的点餐小程序源代码+数据库+文档说明(高分毕设),个人经导师指导并认可通过的毕业设计项目,评审分98分,项目中的源码都是经过本地编译过可运行的,都经过严格调试,确保可以运行!主要针对计算机相关专业的正在做毕业设计的学生和需要项目实战练习的学习者,资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、使用需求,如果有需要的话可以放心下载使用。 基于springboot+vue3+uniapp的点餐小程序源代码+数据库+文档说明(高分毕设)基于springboot+vue3+uniapp的点餐小程序源代码+数据库+文档说明(高分毕设)基于springboot+vue3+uniapp的点餐小程序源代码+数据库+文档说明(高分毕设)基于springboot+vue3+uniapp的点餐小程序源代码+数据库+文档说明(高分毕设)基于springboot+vue3+uniapp的点餐小程序源代码+数据库+文档说明(高分毕设)基于springboot+vue3+uniapp的点餐小程序源代码+数据库+文档说明(高分毕设)基于springb

    欧姆龙NX1P2系列总线plc程序 自动检测机,plc程序,无触摸屏程序 1.多工位DDR马达转盘控制,多工位同时加工 2.多产品配方功能程序 3.各种实用型自制功能块程序,可重复调用,成熟设备

    欧姆龙NX1P2系列总线plc程序 自动检测机,plc程序,无触摸屏程序 1.多工位DDR马达转盘控制,多工位同时加工。 2.多产品配方功能程序。 3.各种实用型自制功能块程序,可重复调用,成熟设备

    企业微信最全养号、防封、加人机制.pdf

    企业微信最全养号、防封、加人机制.pdf

    LLM 友好的异步爬虫框架

    这是一款用 Python 开发的异步爬虫框架,能够将网站上的数据转化成 Markdown、JSON 等 LLM 友好的输出格式。它完全开源且免费,极大地简化了异步爬虫的编写。相比于付费的 Firecrawl,它具有更快的爬取速度,支持同时抓取多个 URL、页面截图、关键字优化提取(基于 LLM)和复杂的多页面会话管理等功能。

    毕设Python春节电影信息爬取与可视化分析源码+项目说明+全部资料.zip

    毕设Python春节电影信息爬取与可视化分析源码+项目说明+全部资料.zip [资源说明] 1、该项目是团队成员近期最新开发,代码完整,资料齐全,含设计文档等 2、上传的项目源码经过严格测试,功能完善且能正常运行,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的高校学生、教师、科研工作者、行业从业者下载使用,可借鉴学习,也可直接作为毕业设计、课程设计、作业、项目初期立项演示等,也适合小白学习进阶,遇到问题不懂就问,欢迎交流。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 5、不懂配置和运行,可远程教学 欢迎下载,学习使用!

    2019厦门国际银行数创金融杯源码+竞赛策略报告文档.zip

    2019厦门国际银行数创金融杯源码+竞赛策略报告文档.zip [资源说明] 1、该项目是团队成员近期最新开发,代码完整,资料齐全,含设计文档等 2、上传的项目源码经过严格测试,功能完善且能正常运行,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的高校学生、教师、科研工作者、行业从业者下载使用,可借鉴学习,也可直接作为毕业设计、课程设计、作业、项目初期立项演示等,也适合小白学习进阶,遇到问题不懂就问,欢迎交流。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 5、不懂配置和运行,可远程教学 欢迎下载,学习使用!

    基于Android Studio开发的安卓的记事本app项目源码(高分期末大作业)

    基于Android Studio开发的安卓的记事本app项目源码(高分期末大作业),个人经导师指导并认可通过的毕业设计项目,评审分98分,项目中的源码都是经过本地编译过可运行的,都经过严格调试,确保可以运行!主要针对计算机相关专业的正在做毕业设计的学生和需要项目实战练习的学习者,资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、使用需求,如果有需要的话可以放心下载使用。 基于Android Studio开发的安卓的记事本app项目源码(高分期末大作业)基于Android Studio开发的安卓的记事本app项目源码(高分期末大作业)基于Android Studio开发的安卓的记事本app项目源码(高分期末大作业)基于Android Studio开发的安卓的记事本app项目源码(高分期末大作业)基于Android Studio开发的安卓的记事本app项目源码(高分期末大作业)基于Android Studio开发的安卓的记事本app项目源码(高分期末大作业)基于Android Studio开发的安卓的记事本app项目源码(高分期末大作业)基于Android Studio开

    基于java的小区智能卡管理系统设计与实现.docx

    基于java的小区智能卡管理系统设计与实现.docx

    NLP中文垃圾短信分类系统源码+设计全部资料+文档报告(自然语言处理课设).zip

    NLP中文垃圾短信分类系统源码+设计全部资料+文档报告(自然语言处理课设).zip [资源说明] 1、该项目是团队成员近期最新开发,代码完整,资料齐全,含设计文档等 2、上传的项目源码经过严格测试,功能完善且能正常运行,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的高校学生、教师、科研工作者、行业从业者下载使用,可借鉴学习,也可直接作为毕业设计、课程设计、作业、项目初期立项演示等,也适合小白学习进阶,遇到问题不懂就问,欢迎交流。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 5、不懂配置和运行,可远程教学 欢迎下载,学习使用!

    电源滤波器车辆状态估计,扩展卡尔曼滤波EKF,无迹卡尔曼滤波UKF车辆状态估计,扩展卡尔曼滤波EKF,无迹卡尔曼滤波UKF 角阶跃输入+整车7自由度模型+UKF状态估计模型+附送EKF状态估计模型,针

    电源滤波器车辆状态估计,扩展卡尔曼滤波EKF,无迹卡尔曼滤波UKF车辆状态估计,扩展卡尔曼滤波EKF,无迹卡尔曼滤波UKF 角阶跃输入+整车7自由度模型+UKF状态估计模型+附送EKF状态估计模型,针对于轮毂电机分布式驱动车辆,进行车速,质心侧偏角,横摆角速度估计。 模型输入:方向盘转角delta,车辆纵向加速度ax 模型输出:横摆角速度wz,纵向车速vx,质心侧偏角β

Global site tag (gtag.js) - Google Analytics