记一次jar包跨project使用问题,引出的对.classpath文件的学习
项目中需要写一个poi导出word工具类,于是引入 jar 包写方法一通操作。测试的时候在同一项目下发现没什么使用问题,在依赖于这个project的另外一个project测试的时候就报错,详细如下,我怎么也就整不明白了
The type org.apache.poi.xwpf.usermodel.XWPFRun cannot be resolved. It is indirectly referenced from required .class files
问题现场
出现这个问题时问题点如下,可能还有其他情况,我只遇到这一种。
我创建了一个类继承 XWPFDocument 这个类,对 XWPFDocument 进行一些拓展。
import org.apache.poi.xwpf.usermodel.XWPFDocument;
/**
* 对原生poi的扩展
* @author Administrator
* @version 0.0.1
*/
public class MyXWPFDocument extends XWPFDocument {
private static Logger logger = LoggerFactory.getLogger(NiceXWPFDocument.class);
protected List<XWPFTable> allTables = new ArrayList<XWPFTable>();
public NiceXWPFDocument() {
super();
}
public MyXWPFDocument(InputStream in) throws IOException {
super(in);
buildAllTables();
}
..... 如下省略,反正 后续的write方法没有,使用的是父类的方法。
另一个 project 中写了一个 Junit 测试方法, 初始化都没问题,在调用 XWPFDocument 方法中的 write (OutputStream stream) 时问题出现了。eclipse总是给我在 write 下面打一个重重的红色错误线。并提示我:The method write(FileOutputStream) is undefined for the type NiceXWPFDocument
但是在 MyXWPFDocument 所在的 project 就不会出现这类问题。
测试代码如下:
@Test
public void testMerge() throws Exception{
MyXWPFDocument doc = new MyXWPFDocument(new FileInputStream(new File("Z:/testFile/docx_render.docx")));
// MyXWPFDocument docMerge = new MyXWPFDocument(new FileInputStream(new File("Z:/testFile/merge_all.docx")));
MyXWPFDocument docMerge = new MyXWPFDocument(new FileInputStream(new File("Z:/testFile/merge_picture.docx")));
doc = doc.merge(docMerge);
FileOutputStream out = new FileOutputStream("out_nice_xwpfdocument.docx");
doc.write(out);
out.close();
}
问题探究
因为 XWPFDocument 是在 Apache poi 的jar包中,eclipse的编译加载机制和其它的 IDE (编辑器)可能不太一样,没使用过其它的IDE不敢瞎逼逼。因为最终项目是要打成war包放在服务器上运行的,这些东西都有啊,为什么不让我用。
再捋一遍问题逻辑。
一个jar包为a.jar。项目b中引用了a.jar,具体为类B继承了a.jar中的类A。
A有两个方法,a.load(),a.write(),B重写了load方法,未重写write方法。
新建了一个Java项目C,C只依赖项目b,未引用a.jar,于是出现了如下两个问题:
1、在C项目中,类B没有write方法
2、在C项目中,会报错: - The type A父类 cannot be resolved. It is indirectly referenced from required .class files
于是我想是不是涉及到类加载的问题,虚拟机加载b中的类并创建对象时,难道不是把所有方法都准备好了吗?为什么还会有父类方法不可见的情况?
之前稍微了解了一点jvm的类加载器,碎片化的东西并没什么卵用。再次了解一下 Java URLClassLoader 和 ClassLoader类加载器,也发现越走越远。直到有人帮我在 .classpath
文件中添加了一个参数 exported="true"
问题解决
在jar包所在的项目的 .classpath文件中添加 exported="true"
,这样在其他项目中使用该jar包的东西时也能查看该 jar包中的依赖关系。
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="generated"/>
<classpathentry kind="lib" path="lib/ooxml-schemas-1.1.jar" exported="true"/>
<classpathentry kind="lib" path="lib/poi-3.10.1.jar" exported="true"/>
<classpathentry kind="lib" path="lib/poi-ooxml-3.10.1.jar" exported="true"/>
<classpathentry kind="lib" path="lib/poi-scratchpad-3.10.1.jar" exported="true"/>
<classpathentry kind="output" path="bin"/>
通过这次问题, 对项目的理解又深了一点。
classpath文件的学习
.classpath是eclipse才有的文件,这点应该是没差了,别的IDE可能没有,到Web服务器中进行部署的话,Web服务器是不会用.classpath来查找,而是通过WEB-INF下面的web.xml为入口顺次启动整个web项目,我估计是使用了URLClassLoader类进行类的加载,这里涉及到了java的类加载机制
.classpath
文件定义项目的结构,如src、output、con、lib等。
- 源文件的具体位置(kind=”src”)
- 运行的系统环境(kind=”con”,exported=”true”)
外部引用的jar(不在项目的libs文件夹中)的具体位置信息(kind=”lib”,exported=”true” ) - 编译后的类文件(*.class)的输出目录(kind=”output”)
output的path指的是编译后导出到相应路径(这里的路径指的不是kind=”output”的路径,output配置只能决定 *.class 文件的输出位置),比如说jar会被导到bin/dexedLibs目录。
exported=true指导出依赖关系,这意味着任何项目,都可以完全的查看和使用该依赖关系。