简体   繁体   English

确定一个类来自哪个 JAR 文件

[英]Determine which JAR file a class is from

I am not in front of an IDE right now, just looking at the API specs.我现在不在 IDE 面前,只是在查看 API 规范。

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
if (src != null) {
    URL jar = src.getLocation();
}

I want to determine which JAR file a class is from.我想确定一个类来自哪个 JAR 文件。 Is this the way to do it?这是这样做的方法吗?

Yes.是的。 It works for all classes except classes loaded by bootstrap classloader.它适用于除引导类加载器加载的类之外的所有类。 The other way to determine is:另一种确定方法是:

Class klass = String.class;
URL location = klass.getResource('/' + klass.getName().replace('.', '/') + ".class");

As notnoop pointed out klass.getResource() method returns the location of the class file itself.正如 notnoop 指出的那样, klass.getResource()方法返回类文件本身的位置。 For example:例如:

jar:file:/jdk/jre/lib/rt.jar!/java/lang/String.class
file:/projects/classes/pkg/MyClass$1.class

The getProtectionDomain().getCodeSource().getLocation() method returns the location of the jar file or CLASSPATH getProtectionDomain().getCodeSource().getLocation()方法返回jar文件的位置或CLASSPATH

file:/Users/home/java/libs/ejb3-persistence-1.0.2.GA.jar
file:/projects/classes

Checkout the LiveInjector.findPathJar() from Lombok Patcher LiveInjector.java .Lombok Patcher LiveInjector.java查看LiveInjector.findPathJar() Note that it special cases where the file doesn't actually live in a jar, and you might want to change that.请注意,在文件实际上并不存在于 jar 中的特殊情况下,您可能想要更改它。

/**
 * If the provided class has been loaded from a jar file that is on the local file system, will find the absolute path to that jar file.
 * 
 * @param context The jar file that contained the class file that represents this class will be found. Specify {@code null} to let {@code LiveInjector}
 *                find its own jar.
 * @throws IllegalStateException If the specified class was loaded from a directory or in some other way (such as via HTTP, from a database, or some
 *                               other custom classloading device).
 */
public static String findPathJar(Class<?> context) throws IllegalStateException {
    if (context == null) context = LiveInjector.class;
    String rawName = context.getName();
    String classFileName;
    /* rawName is something like package.name.ContainingClass$ClassName. We need to turn this into ContainingClass$ClassName.class. */ {
        int idx = rawName.lastIndexOf('.');
        classFileName = (idx == -1 ? rawName : rawName.substring(idx+1)) + ".class";
    }

    String uri = context.getResource(classFileName).toString();
    if (uri.startsWith("file:")) throw new IllegalStateException("This class has been loaded from a directory and not from a jar file.");
    if (!uri.startsWith("jar:file:")) {
        int idx = uri.indexOf(':');
        String protocol = idx == -1 ? "(unknown)" : uri.substring(0, idx);
        throw new IllegalStateException("This class has been loaded remotely via the " + protocol +
                " protocol. Only loading from a jar on the local file system is supported.");
    }

    int idx = uri.indexOf('!');
    //As far as I know, the if statement below can't ever trigger, so it's more of a sanity check thing.
    if (idx == -1) throw new IllegalStateException("You appear to have loaded this class from a local jar file, but I can't make sense of the URL!");

    try {
        String fileName = URLDecoder.decode(uri.substring("jar:file:".length(), idx), Charset.defaultCharset().name());
        return new File(fileName).getAbsolutePath();
    } catch (UnsupportedEncodingException e) {
        throw new InternalError("default charset doesn't exist. Your VM is borked.");
    }
}

Use

String path = <Any of your class within the jar>.class.getProtectionDomain().getCodeSource().getLocation().getPath(); 

If this contains multiple entries then do some substring operation.如果这包含多个条目,则执行一些子字符串操作。

private String resourceLookup(String lookupResourceName) {



    try {

        if (lookupResourceName == null || lookupResourceName.length()==0) {
            return "";
        }
        // "/java/lang/String.class"

        // Check if entered data was in java class name format
        if (lookupResourceName.indexOf("/")==-1) {
            lookupResourceName = lookupResourceName.replaceAll("[.]", "/");
            lookupResourceName =  "/" + lookupResourceName + ".class";
        }

        URL url = this.getClass().getResource(lookupResourceName);
        if (url == null) {
            return("Unable to locate resource "+ lookupResourceName);

        }

        String resourceUrl = url.toExternalForm();

        Pattern pattern =
            Pattern.compile("(zip:|jar:file:/)(.*)!/(.*)", Pattern.CASE_INSENSITIVE);

        String jarFilename = null;
        String resourceFilename = null;
        Matcher m = pattern.matcher(resourceUrl);
        if (m.find()) {
            jarFilename = m.group(2);
            resourceFilename = m.group(3);
        } else {
            return "Unable to parse URL: "+ resourceUrl;

        }

        if (!jarFilename.startsWith("C:") ){
          jarFilename = "/"+jarFilename;  // make absolute path on Linux
        }

        File file = new File(jarFilename);
        Long jarSize=null;
        Date jarDate=null;
        Long resourceSize=null;
        Date resourceDate=null;
        if (file.exists() && file.isFile()) {

            jarSize = file.length();
            jarDate = new Date(file.lastModified());

            try {
                JarFile jarFile = new JarFile(file, false);
                ZipEntry entry = jarFile.getEntry(resourceFilename);
                resourceSize = entry.getSize();
                resourceDate = new Date(entry.getTime());
            } catch (Throwable e) {
                return ("Unable to open JAR" + jarFilename + "   "+resourceUrl +"\n"+e.getMessage());

            }

           return "\nresource: "+resourceFilename+"\njar: "+jarFilename + "  \nJarSize: " +jarSize+"  \nJarDate: " +jarDate.toString()+"  \nresourceSize: " +resourceSize+"  \nresourceDate: " +resourceDate.toString()+"\n";


        } else {
            return("Unable to load jar:" + jarFilename+ "  \nUrl: " +resourceUrl);

        }
    } catch (Exception e){
        return e.getMessage();
    }


}

With Linux, I'm using a small script to help me find in which jar a class lies that can be used in a find -exec :在 Linux 中,我使用一个find -exec帮助我找到可以在find -exec使用的类位于哪个 jar 中:

findclass.sh : findclass.sh :

unzip -l "$1" 2>/dev/null | grep $2 >/dev/null 2>&1 && echo "$1"

Basically, as jars are zip, unzip -l will print the list of class resources, so you'll have to convert .基本上,由于 jar 是 zip,因此unzip -l将打印类资源列表,因此您必须将. to / ./ You could perform the replacement in the script with a tr , but it's not too much trouble to do it yourself when calling the script.您可以使用tr在脚本中执行替换,但在调用脚本时自己完成并不会太麻烦。

The, the idea is to use find on the root of your classpath to locate all jars, then runs findclass.sh on all found jars to look for a match.这个想法是在类路径的根目录上使用find来定位所有 jar,然后在所有找到的 jar 上运行findclass.sh以查找匹配项。

It doesn't handle multi-directories, but if you carefully choose the root you can get it to work.它不处理多目录,但如果您仔细选择根目录,您可以使其工作。

Now, find which jar contains class org.apache.commons.lang3.RandomUtils to you un-mavenize your project (...):现在,找到哪个 jar 包含类org.apache.commons.lang3.RandomUtils来取消对项目的优化(...):

$ find ~/.m2/repository/ -type f -name '*.jar' -exec findclass.sh {} org/apache/commons/lang3/RandomUtils \;

.m2/repository/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar
.m2/repository/org/apache/commons/commons-lang3/3.6/commons-lang3-3.6.jar
.m2/repository/org/apache/commons/commons-lang3/3.6/commons-lang3-3.6-sources.jar
$

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM