9


从我的 java (ant) 项目中删除未使用的 jar 的最简单方法是什么(工具?)。我们的项目变得非常庞大,我们想做一个清理工作。类路径中添加了几个 jar,但并非所有 jar 都用于编译/运行。有没有办法通过从命令行运行一些实用程序来识别不必要的 jar?
注意:我们不想遵循删除一个或多个罐子的繁琐过程,然后编译/运行以测试是否需要这些罐子。

4

4 回答 4

11

这就是我最后所做的,
我使用 JBOSS TattleTale ( http://www.jboss.org/tattletale ) 在编译时识别未使用的 jars(它报告 17 个 jars 未使用)。
然后对于该列表中的每个 jar,我对我的项目进行了字符串搜索,以查找是否在任何地方使用了 jar 的最高包级别(这是尝试查看是否使用反射加载了属于该 jar 的任何类) .
这样我就能够成功地从我的项目中消除 6 个罐子。
由于编译时未使用的 jar 数量很少,因此这种方法很快。

于 2013-10-02T14:37:36.430 回答
6

如果有更好的解决方案,我很想听听。

问题是即使一个 jar 不需要编译,它也可能需要运行。而这种“需要”可能是传递的。EG:您使用的 jar 需要一个您不使用且仅在运行时使用的 jar。与运行时错误的本质一样,它们只会在您尝试执行代码时显示出来。最坏的情况是,在您投入生产之前可能不会发生这种情况。

我所知道的最佳解决方案正是您不想做的:“删除一个或多个 jar,然后编译/运行以测试是否需要这些 jar”。

于 2013-09-30T19:45:05.930 回答
2

删除 -> 测试 -> 删除 -> 测试 -> 删除 -> 希望你喜欢 :)

将项目更改为 maven 并仅选择您要使用的 jar。对我来说这是最简单的方法。

于 2013-09-30T20:21:00.370 回答
1

正如 tieTYT 在他的回答中指出的那样,许多 jar 仅在编译时才需要。(例如 jdbc 实现 jars)

另一种方法可能是编写一个 java 代理并收集所有类名。然后,您可以将这些类名映射到 jar 并查看是否不需要任何 jar。

样品代理

package com;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;


public class NameAgent {


    public static void premain(String agentArgs, Instrumentation inst) {

        inst.addTransformer(new Tranformer(), true);
    }


    static class Tranformer implements ClassFileTransformer {

        @Override
        public byte[] transform(ClassLoader loader,
                                String className,
                                Class<?> classBeingRedefined,
                                ProtectionDomain protectionDomain,
                                byte[] classfileBuffer) throws IllegalClassFormatException {
            System.out.println("loading-class:" + className);
            return classfileBuffer;
        }
    }

}

Build.xml... 用于蚂蚁

   <project name="namedump" default="dist" basedir=".">

    <property name="src" value="src"/>
    <property name="build" value="build"/>
    <property name="dist" value="dist"/>

    <target name="init">
        <mkdir dir="${build}"/>
        <mkdir dir="${dist}"/>
    </target>

    <target name="compile" depends="init">
        <javac srcdir="${src}" destdir="${build}" optimize="true" includeantruntime="false"/>
    </target>

    <target name="dist" depends="compile">
        <jar jarfile="${dist}/namecollector.jar">
            <fileset dir="${build}"/>
                <manifest >
                    <attribute name="Premain-Class" value="com.NameAgent" />
                    <attribute name="Can-Redefine-Classes" value="true" />
                    <attribute name="Can-Retransform-Classes" value="true" />

                </manifest>
        </jar>
        <pathconvert property="absdist" dirsep="/">
            <path location="${dist}"/>
        </pathconvert>
        <echo>
            To use, add the following to the JVM command-line options.

            -javaagent:${absdist}/namecollector.jar
        </echo>
    </target>

    <target name="clean">

        <delete dir="${build}"/>
        <delete dir="${dist}"/>
    </target>
</project>

我们在Windows中尝试的另一种方法是使用加载类时 jar 被锁定的事实。运行应用程序并尝试从运行时区域中删除。如果使用它,删除应该会失败。不确定这有多安全-我知道它在 linux 等上不起作用,可以毫无问题地删除文件。

于 2013-10-01T06:44:20.700 回答