记一次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指导出依赖关系,这意味着任何项目,都可以完全的查看和使用该依赖关系。

posted @ 2019-07-15 14:20  Monkey_Hi_猴子喂  阅读(748)  评论(0编辑  收藏  举报