Xác định tệp JAR mà một lớp đến từ


154

Tôi không ở trước một IDE ngay bây giờ, chỉ nhìn vào thông số kỹ thuật API.

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

Tôi muốn xác định tệp JAR mà một lớp đến từ đâu. Đây có phải là cách để làm điều đó?


2
Có cách nào để làm điều này từ console không? Một cái gì đó như java -findjar -cp /some/path/with/libs/*.jar my.java.Class-> my.jar.
kub1x

Câu trả lời:


191

Đúng. Nó hoạt động cho tất cả các lớp trừ các lớp được tải bởi trình nạp lớp bootstrap. Cách khác để xác định là:

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

Như notnoop đã chỉ ra klass.getResource()phương thức trả về vị trí của tệp lớp. Ví dụ:

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

Các getProtectionDomain().getCodeSource().getLocation()phương thức trả về vị trí của file jar hoặc CLASSPATH

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

Điều này làm cho các giả định tiếp tục ánh xạ từ tên lớp sang tệp lớp. Nó sẽ hoạt động đúng cho các lớp ẩn danh? Các lớp lồng nhau?
Thorbjørn Ravn Andersen

1
Điều này trỏ đến url của lớp chứ không phải chính jar. Url cần được phân tích cú pháp để tìm tệp jar.
notnoop

@ chú thích. Tôi đã làm rõ câu trả lời.
Chandra Patni

19
Nó chính xác theo cách khác như được viết trong câu trả lời, hãy sử dụng getProtectionDomain().getCodeSource().getLocation()nếu bạn muốn lấy vị trí tệp jar
peter

Cảm ơn câu trả lời này, nó đã truyền cảm hứng cho tôi để trả lời câu hỏi này .
kriegaex

12

Kiểm tra LiveInjector.findPathJar()từ Lombok PatcherLiveInjector.java . Lưu ý rằng đó là trường hợp đặc biệt khi tệp không thực sự sống trong một cái bình và bạn có thể muốn thay đổi điều đó.

/**
 * 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.");
    }
}

2
Điều này có vẻ quá phức tạp để có được một cái gì đó rất đơn giản. Tôi chỉ ngồi xuống và thử những gì tôi tìm thấy trước đó, và nó dường như hoạt động. Chỉ muốn một số xác nhận.

Tốt. Mã của bạn không xử lý các tệp trong đường dẫn bootgroup và giải pháp của Chandra trả lại url cho tệp không phải tệp jar, vì vậy bạn sẽ cần phân tích đường dẫn để tìm tệp jar.
notnoop

2

Sử dụng

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

Nếu cái này chứa nhiều mục thì hãy thực hiện một số thao tác chuỗi con.


1
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();
    }


}

Đoạn mã trên sẽ tìm thấy bất kỳ tài nguyên nào trên đường dẫn. Nếu trong một Jar sẽ tìm thấy bình in kích thước và ngày của bình và kích thước và ngày của tài nguyên trong bình
Don
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.