Java Jar : sealed in manifest

在上一篇文章中说到了Manifest.mf文件中可以通过Sealed属性来指定某些包是否是密封的。那么到底什么是密封的,如何来理解它呢?

对于sealed,官方文档中的说法如下:

    JAR files and packages can be optionally sealed so that an package can enforce consistency within a version.
    A package sealed within a JAR specifies that all classes defined in that package must originate from the same JAR. Otherwise, a SecurityException is thrown.
    如果一个package通过JAR文件清单指定了sealed,那么这个包下的所有的类都必须是出自同一个jar文件。不然的话,就出抛出一个SecurityException。

为了解决这个疑惑,来做几个测试吧,通过测试来了解sealed:

1:在一个package中随便写上两个类:ClassA、ClassB:

在com.fjn.java.util包下有:

ClassA:

package com.fjn.java.util.jar;
/**
 *
 * @author [email protected] 2015年7月10日
 *
 */
public class ClassA {
    String id = "100";
    String name = "hello";

    public void showInfo() {
        System.out.println(this);
    }

    @Override
    public String toString() {
        return "id: " + this.id + ", name: " + this.name;
    }
}

ClassB:

package com.fjn.java.util.jar;
/**
 *
 * @author [email protected] 2015年7月10日
 *
 */
public class ClassB {
    public static void main(String[] args) {
        ClassA obj=new ClassA();
        obj.name="hello ,java sealed";
        obj.showInfo();
    }
}

2:打包并设置不sealed

现在打包成两个包(打包时,都设置不sealed):

1)只将ClassA打进包中,打包为java_sealed_v1.jar

2)将com.fjn.java.util整体打包,名字是:java_sealed_v2.jar

java_sealed_v1.jar的清单:

Manifest-Version: 1.0

Name: com/fjn/java/util/jar/
Sealed: fasle

java_sealed_v2.jar的清单:

Manifest-Version: 1.0
Sealed: false

第3步:写测试用例

创建一个新的project,导入这两个jar。测试类如下:

package com.java.sealtest;

import com.fjn.java.util.jar.ClassA;
import com.fjn.java.util.jar.ClassB;

public class SealedTest {

    public static void main(String[] args) {
        ClassA objA=new ClassA();
        System.out.println(objA);
        System.out.println(Package.getPackage("com.fjn.java.util.jar").isSealed());
        System.out.println(objA.getClass().getProtectionDomain().getCodeSource().getLocation());

        ClassB objB=new ClassB();
        System.out.println(objB);
        System.out.println(Package.getPackage("com.fjn.java.util.jar").isSealed());
        System.out.println(objB.getClass().getProtectionDomain().getCodeSource().getLocation());
        ClassB.main(new String[0]);
    }
}


4步:进行测试

测试1)都不使用sealed

执行上述测试用例,结果如下:

id: 100, name: hello
false
file:/E:/workspace/Test/lib/java_sealed_v1.jar
[email protected]
false
file:/E:/workspace/Test/lib/java_sealed_v2.jar
id: 100, name: hello ,java sealed

该测试执行成功,从结果中可以看出,在ClassA 类是从java_sealed_v1.jar中加载的、ClassB是从java_sealed_v2.jar中加载的。

测试2)java_sealed_v1.jar中的sealed启用。

将java_sealed_v1.jar manifest.mf中的sealed设置为true,此时:

java_sealed_v1.jar#manifest.mf:

Manifest-Version: 1.0

Name: com/fjn/java/util/jar/
Sealed: true

java_sealed_v2.jar#manifest.mf:

Manifest-Version: 1.0
Sealed: false

执行测试,结果如下:

id: 100, name: hello
true
file:/E:/workspace/Test/lib/java_sealed_v1.jar
Exception in thread "main" java.lang.SecurityException: sealing violation: package com.fjn.java.util.jar is sealed
    at java.net.URLClassLoader.getAndVerifyPackage(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$100(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at com.java.sealtest.SealedTest.main(SealedTest.java:14)

从这个结果上看,应该是程序执行到ClassB objB=new ClassB();这句时出错了。

在执行这个语句时,要加载ClassB,jvm在java_sealed_v2.jar中找到了ClassB,找到后要执行getAndVerifyPackage方法。在这个过程中出错。

现在来看一下URLClassLoader#getAndVerifyPackage()方法:

private Package getAndVerifyPackage(String pkgname,
                                        Manifest man, URL url) {
// 从当前ClassLoader已经加载的包集合中查找,这个包是否已经加载过了
// 如果已经加载过了,返回值pkg就不是null.
        Package pkg = getPackage(pkgname);
        if (pkg != null) {
            // Package found, so check package sealing.
            if (pkg.isSealed()) {
                // Verify that code source URL is the same.
                if (!pkg.isSealed(url)) {
                    throw new SecurityException(
                        "sealing violation: package " + pkgname + " is sealed");
                }
            } else {
                // Make sure we are not attempting to seal the package
                // at this code source URL.
                if ((man != null) && isSealed(pkgname, man)) {
                    throw new SecurityException(
                        "sealing violation: can‘t seal package " + pkgname +
                        ": already loaded");
                }
            }
        }
        return pkg;
    }

从ClassLoader已经加载的包中找到了java_sealed_v1.jar下的com.fjn.java.util.jar 包,这个包是密封的,所以就抛出错误了。

从上面这段代码,还能看出另外一个问题:如果一个未密封的包被加载了,再次加载同包名不同jar文件中类时,也会出错。

测试3)java_sealed_v1.jar中的sealed禁用、java_sealed_v2.jar中的sealed启用。这个测试就是用于验证上面说的另外一种情况的。

此时清单状态如下:

java_sealed_v1.jar#manifest.mf:

Manifest-Version: 1.0

Name: com/fjn/java/util/jar/
Sealed: false

java_sealed_v2.jar#manifest.mf:

Manifest-Version: 1.0
Sealed: true

测试结果如下:

id: 100, name: hello
false
file:/E:/workspace/Test/lib/java_sealed_v1.jar
Exception in thread "main" java.lang.SecurityException: sealing violation: can‘t seal package com.fjn.java.util.jar: already loaded
    at java.net.URLClassLoader.getAndVerifyPackage(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$100(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at com.java.sealtest.SealedTest.main(SealedTest.java:14)

测试结果验证了上面的说法。

从这几个测试中知道:

在加载类的时候,如果要加载的类 所在的包,在多个jar文件中,只要有一个被指定了sealed,运行时就有可能出现问题。

如果一个package(package名相同即为同一个包),存在于多个jar文件中,最好是都不要限制为sealed。

在一个project中,如果某个jar多个版本共存时,一定要注意sealed的设置。

时间: 2025-01-09 01:20:42

Java Jar : sealed in manifest的相关文章

Java Jar :Manifest

Jar (Java Archive File, java归档文件),它其实也是一个zip文件.它包括了一个可选的META-INF目录.可以使用命令行中的jar命令来生成一个jar文件,也可以使用java.util.jar API来生成一个归档文件. Jar文件中META-INF目录的作用:应用配置.扩展.类加载器.提供服务.在这个目录下,主要有下列几个文件(或者目录): MANIFEST.MF 是一个清单文件,定义了扩展和与包相关的信息 INDEX.LIST 它是JarIndex实现的一部分,类

Jar文件 META-INF/MANIFEST.MF文件详解

打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF目录, 这个目录下会有一些文件,其中必有一个MANIFEST.MF,这个文件描述了该Jar文件的很多信息,下面将详细介绍MANIFEST.MF文件的内 容,先来看struts.jar中包含的MANIFEST.MF文件内容: Manifest-Version: 1.0Created-By: Apache Ant 1.5.1Extension-Name: Struts FrameworkSpecification-Title:

java -cp 命令 java jar 命令和 hadoop jar 命令

-cp 和 -classpath 一样,是指定类运行所依赖其他类的路径,通常是类库,jar包之类,需要全路径到jar包,window上分号";" java -cp .;myClass.jar packname.mainclassname   classpath中的jar文件能使用通配符,如果是多个jar文件,要一个一个地罗列出来,从某种意义上说jar文件也就是路径. 要指定各个JAR文件具体的存放路径,相同路径有多个可使用通配符 java -cp .;c:\classes\myClas

java jar打包命令使用

用法:jar {ctxu}[vfm0Mi] [jar-文件] [manifest-文件] [-C 目录] 文件名 ... 选项: -c 创建新的存档 -t 列出存档内容的列表 -x 展开存档中的命名的(或所有的]文件 -u 更新已存在的存档 -v 生成详细输出到标准输出上 -f 指定存档文件名 -m 包含来自标明文件的标明信息 -0 只存储方式:未用zip压缩格式 -M 不产生所有项的清单(manifest]文件 -i 为指定的jar文件产生索引信息 -C 改变到指定的目录,并且包含下列文件:

用JSmooth制作java jar文件的可运行exe文件教程【图文】

这是我之前在个人博客3yj上面写的一篇文章,如今转载过来,原文地址 (这不是广告哦) 几年前,刚接触java的是,就想用一些方法把自己的劳动果实保护起来,曾经也用过非常多这种工具,有一个特别好用,今天写篇文章跟大家分享. 今天要用到的工具有 JSmooth 0.9.9-7 汉化版,能够到本站下载:JSmooth 0.9.9-7 汉化版 首先要把你的程序打包成双击能够运行的包,详细的方法我就不多说了,网上非常多的,主要命令是:jar cvfm test.jar *.* -C  之类的 主要是确保其

Eclipse导出Jar包 和 MANIFEST.MF文件

Eclipse 导出Jar包,在工程中File-Export-JAR file-Next...-Finish,注意选择自己的程序Main Class,其中Jar包中的MANIFEST.MF文件包含了jar的主程序入口和依赖jar的存放位置等信息. Java -jar  Test.jar 导出Jar包后,如有外部包依赖,还要修改Jar包中的 MANIFEST.MF 文件 Manifest-Version: 1.0 Main-Class: modification.AppFrame Class-Pa

Java jar命令 常见用法

一.jar命令作用: 进行打包 -- 把多个文件打包成一个压缩包 -- 这个压缩包和Winzip的压缩格式是一样的. 区别在于jar压缩的文件默认多一个META-INF的文件夹,该文件夹下包含一个Manifest.mf(清单文件)的文件 通常来说jar命令得到的压缩包有三种(压缩格式完全一样,只是后缀名不同而已): A.*.jar - 它里面包含N个class文件. B.*.war (web) - 它是一个web应用打包生成的. C.*.ear(Enterprise) -它是一个企业应用打包生成

使用java -jar运行jar包

使用java -jar运行jar包 摘要 其实不难,网上搜了不少,但是讲不全,或者讲得不少,有的细节却漏掉了, 在此,我想将详细点 java -jar 1.其实命令比较简单:进入相应的目录后,执行java -jar XXX.jar即可! 2.但是,为什么通过Maven打包或者Eclipse export出来的jar不能运行呢?原因很简单,没指定执行哪个类的main方法,如何指定?配置文件,项目文件夹下有一个META-INF文件夹,META-INF文件夹下有一个MANIFEST.MF文件,只要更改

java jar bat 运行

1.    添加config.properties配制文件 2.    读取配制文件 in = new FileInputStream(System.getProperty("user.dir")+"\\lib\\config.properties"); prop.load(in); 3.    导出代码为jar 4.    添加依赖的jar包到MANIFEST.MF,jar放到同目录下 Manifest-Version: 1.0 ??Class-Path: dom