java代码中调用受限制的JDK API

每个故事的开端都是由一个异于常人的勇士引起,正巧我身边也有这么一位勇士。在最近的一个小项目中(jdk7.0.XX+eclipse+maven+tomcat 7.0.XX),有同事使用了以下的两个类,具体是用来做什么没有去过多了解。

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
当第一眼看到这两个家伙的时候,只是觉得有点高大上,毕竟人家的开头可是“com.sun”,贵族之气十足,事实也是这样的,这两个类都位于rt.jar包中。下面看看他带来的问题:

  1.  在服务器上使用 mvn package 命令进行打包,出现compilation error,程序包com.sun.image.codec.jpeg不存在,导致无法打包,项目无法部署。
  2. 在eclipse环境下执行mvn package命令正常执行,在开发环境执行mvn package 命令正常执行。

到了这个时候不得不去看看这两个类是何方神圣,点开javadoc看到这么一段话:

Note that the classes in the com.sun.image.codec.jpeg package are not part of the core Java APIs.  They are a part of Sun's JDK and JRE distributions.  Although other licensees may choose to distribute these classes, developers cannot depend on their availability in non-Sun implementations.  We expect that equivalent functionality will eventually be available in a core API or standard extension.

大意是说:com.sun.image.codec.jpeg包里面的类不是core Java APIs,它们是Sun‘s JDK和JRE的一部分。尽管有些人会把这些类分发出去,但是开发者不应该依赖这些类进行开发。未来会有同样的功能在core APIs中出现的,敬请期待。

至此意思已经很明白了,“你丫的用了我们不想你用的东西,我已经提前跟编译器打了招呼,这样的统统不允许编译通过”,“好吧,既然你说了给编译器打了招呼的,为什么eclipse就可以编译过去”,“这这这.....eclipse他不按规矩来,那是在害你。” 娱乐娱乐......

目前到手的信息就这么多,果断以那两个字作为关键字拿来问度娘,哈哈,度娘效率不是盖的,答案揭晓:eclipse>Window>Preferences>Compiler>Errors/Warnings 然后再找到Deprecated and restricted API 下面的Forbidden references(access rules) 后面的Error切换为Warning。可是,可是一开始的时候编译器就没有报错都嘛。继续搜,OH,大家都是这么说的,看来他们都是一个老师教的。这个方案可以用来解决在IDE中产生的编译不通过的情况,如果你把项目打包也是使用IDE的话,这个问题就完美解决了。

可是我的问题是在服务器上使用maven进行打包的时候出现compilation error,实在不知道怎么解决了,于是去找大神咨询,大神一听我的描述,同样是在度娘那里,这次他输入的是“maven rt.jar”这么两个关键字,so我要找的答案来了。按照文章所说的修改了maven-compiler-plugin的配置,将编译器参数:bootclasspath加上。当我还在心目中膜拜大神的时候,这边的的程序也已经重新启动完了,一个好消息,一个坏消息,之前的问题消失了,又来了一个新问题,另外的一些类又找不到了,就是这些:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
这几个类是jce.jar包里面的跟rt.jar都是jre>lib里面的jar包,前面那个bootclasspath的作用是用来指定编译器进行文件编译的时候需要依赖的core APIs,当然不指定的话编译器自己也是知道去哪里找到这些引导类的。问题到了这里,没头绪了。接下里就从baidu转战谷歌,又找到了一些更加合理的见解,博文我会在文章的后面给出的,供参考。在前面的过程中,我掌握了一项新技能那就是编译器参数 -verbose(显示编译器编译过程的详细信息),正是这个参数打开了这个问题的另一扇窗,下面是为编译器加上这个参数以后的输出(这是没有加上-bootclasspath:${java.home}/lib/rt.jar参数之前的):

[解析开始时间 RegularFileObject[E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\src\main\java\com\yiji\yrt\themisclient\ThemisClientInit.java]]
[解析已完成, 用时 12 毫秒]
[源文件的搜索路径: E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\src\main\java,E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\target\generated-sources\annotations]
[类文件的搜索路径: D:\Java\jdk1.7.0_40\jre\lib\resources.jar,D:\Java\jdk1.7.0_40\jre\lib\rt.jar,D:\Java\jdk1.7.0_40\jre\lib\sunrsasign.jar,D:\Java\jdk1.7.0_40\jre\lib\jsse.jar,D:\Java\jdk1.7.0_40\jre\lib\jce.jar,D:\Java\jdk1.7.0_40\jre\lib\charsets.jar,D:\Java\jdk1.7.0_40\jre\lib\jfr.jar,D:\Java\jdk1.7.0_40\jre\classes,D:\Java\jdk1.7.0_40\jre\lib\ext\access-bridge.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\dnsns.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\jaccess.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\localedata.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunec.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunjce_provider.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunmscapi.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunpkcs11.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\zipfs.jar,E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\target\classes,E:\m2\repository\org\mapu\themis\themis-sdk\1.1.1\themis-sdk-1.1.1.jar,E:\m2\repository\com\github\rop\rop-client\2.0\rop-client-2.0.jar,E:\m2\repository\com\github\rop\rop-common\2.0\rop-common-2.0.jar,E:\m2\repository\org\slf4j\slf4j-api\1.7.7\slf4j-api-1.7.7.jar,E:\m2\repository\commons-codec\commons-codec\1.9\commons-codec-1.9.jar,E:\m2\repository\commons-io\commons-io\2.4\commons-io-2.4.jar,E:\m2\repository\org\apache\httpcomponents\httpclient\4.3.2\httpclient-4.3.2.jar,E:\m2\repository\org\apache\httpcomponents\httpcore\4.3.1\httpcore-4.3.1.jar,E:\m2\repository\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar,E:\m2\repository\javax\wsdl\javax.wsdl\1.4.0.v200706111329\javax.wsdl-1.4.0.v200706111329.jar,E:\m2\repository\com\google\guava\guava\15.0\guava-15.0.jar,E:\m2\repository\org\springframework\spring-core\3.2.6.RELEASE\spring-core-3.2.6.RELEASE.jar,.]
[正在加载ZipFileIndexFileObject[E:\m2\repository\com\google\guava\guava\15.0\guava-15.0.jar(com/google/common/collect/Maps.class)]]
[正在加载ZipFileIndexFileObject[E:\m2\repository\org\apache\httpcomponents\httpclient\4.3.2\httpclient-4.3.2.jar(org/apache/http/client/config/RequestConfig.class)]]
[正在加载ZipFileIndexFileObject[E:\m2\repository\org\mapu\themis\themis-sdk\1.1.1\themis-sdk-1.1.1.jar(org/mapu/themis/ThemisClient.class)]]
[正在加载ZipFileIndexFileObject[E:\m2\repository\org\springframework\spring-core\3.2.6.RELEASE\spring-core-3.2.6.RELEASE.jar(org/springframework/core/io/Resource.class)]]
[正在加载ZipFileIndexFileObject[E:\m2\repository\org\springframework\spring-core\3.2.6.RELEASE\spring-core-3.2.6.RELEASE.jar(org/springframework/core/io/support/PathMatchingResourcePatternResolver.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\jre\lib\jce.jar(javax/crypto/Cipher.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\jre\lib\jce.jar(javax/crypto/SecretKey.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\jre\lib\jce.jar(javax/crypto/spec/SecretKeySpec.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\lib\<strong>ct.sym(META-INF/sym/rt.jar</strong>/java/io/IOException.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\lib\<strong>ct.sym(META-INF/sym/rt.jar</strong>/java/util/ArrayList.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\lib\<strong>ct.sym(META-INF/sym/rt.jar</strong>/java/util/Enumeration.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\lib\ct.sym(META-INF/sym/rt.jar/java/util/Map.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\lib\ct.sym(META-INF/sym/rt.jar/java/util/Properties.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Object.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\lib\ct.sym(META-INF/sym/rt.jar/java/lang/String.class)]]
这段日志就是为javac 加上-verbose参数以后打印出来的,说明了源文件的搜索路径,以及类文件的搜索路径,类文件的路径就是项目依赖的那些jar包:1.jre/lib下面的2.项目依赖的第三方jar包。编译的过程中就需要加载一些class文件,这里可以看到jce.jar里面的class文件被正常加载了,但是日志中没有打印加载JPEGCodec和JPEGImageEncoder两个类的日志。还有就是javac没有按照我们的想法去加载${java_home}\jre\lib\rt.jar这个jar包,而是去${java_home}/lib/ct.sym里面加载了另外一个rt.jar。所以问题就在这里了,肯定ct.sym里面的那个rt.jar跟${java_home}/jre/lib/rt.jar的内容是不一样的。ct.sym也是第一次在我的知识范围中出现,于是展开了一通搜索,大概找到了他道题是个什么东东:

The standard build is done using the javac from your JDK. The javac since JDK6 does not compile against rt.jar, but against a special symbol file (lib/ct.sym). This file does not contain private classes introduced in JDK6.
这段话摘自一篇netbeans的bug修复记录中,一位netbeans的人员给出的。在jdk6的时候做了一个改进,通过这个ct.sym文件把jdk中的私有(这里的私有应该是指不允许jdk外部的application使用)类给屏蔽掉了。javac进行编译的时候不在直接的使用${java_home}/jre/lib/rt.jar而是使用${java_home}/lib/ct.sym里面封装的那个rt.jar.毫无疑问最初的两个类
JPEGCodec和JPEGImageEncoder就是在被屏蔽的范围内,不建议大家使用。接下来又给出来解决方案:

If needed, the current javac allows to override this behavior using the following compiler argument:
-XDignore.symbol.file
(AFAIK this is a private option that may be removed in the future.)
说的已经很清楚了,这个参数是个不稳定的,很可能在后面就会消失,这个bug记录的时间是2010年,现在已经是14年了,我使用这个-XDignore.symbol.file的时候,这个参数已经失效了。或许这才是这个问题的最完美的解决方案,可是这个问题一开始就是一个不遵守规则而带来恶果,恶有恶报咯。

到这里中间的很多疑惑已经解决了,那就是javac 在使用standard编译方式进行文件编译的时候使用了ct.sym这个文件,没有按照期望使用jre/lib/rt.jar文件。

路子总是这么不经意的走出来的,文章的开始得到大神指点后不是找到了一个解决方案么,那个方案就是把rt.jar给替换掉,这个时候已经掌握了“神器” -verbose的用法,就忍不住要看看改变了bootclasspath以后的javac是怎么工作的,下边贴上log:

[解析开始时间 RegularFileObject[E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\src\main\java\com\yiji\yrt\themisclient\ThemisClientInit.java]]
[解析已完成, 用时 14 毫秒]
[源文件的搜索路径: E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\src\main\java,E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\target\generated-sources\annotations]
[类文件的搜索路径: D:\Java\jdk1.7.0_40\jre\lib\rt.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\access-bridge.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\dnsns.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\jaccess.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\localedata.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunec.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunjce_provider.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunmscapi.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunpkcs11.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\zipfs.jar,E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\target\classes,E:\m2\repository\org\mapu\themis\themis-sdk\1.1.1\themis-sdk-1.1.1.jar,E:\m2\repository\com\github\rop\rop-client\2.0\rop-client-2.0.jar,E:\m2\repository\com\github\rop\rop-common\2.0\rop-common-2.0.jar,E:\m2\repository\org\slf4j\slf4j-api\1.7.7\slf4j-api-1.7.7.jar,E:\m2\repository\commons-codec\commons-codec\1.9\commons-codec-1.9.jar,E:\m2\repository\commons-io\commons-io\2.4\commons-io-2.4.jar,E:\m2\repository\org\apache\httpcomponents\httpclient\4.3.2\httpclient-4.3.2.jar,E:\m2\repository\org\apache\httpcomponents\httpcore\4.3.1\httpcore-4.3.1.jar,E:\m2\repository\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar,E:\m2\repository\javax\wsdl\javax.wsdl\1.4.0.v200706111329\javax.wsdl-1.4.0.v200706111329.jar,E:\m2\repository\com\google\guava\guava\15.0\guava-15.0.jar,E:\m2\repository\org\springframework\spring-core\3.2.6.RELEASE\spring-core-3.2.6.RELEASE.jar,.]
[正在加载ZipFileIndexFileObject[E:\m2\repository\com\google\guava\guava\15.0\guava-15.0.jar(com/google/common/collect/Maps.class)]]
[正在加载ZipFileIndexFileObject[E:\m2\repository\org\apache\httpcomponents\httpclient\4.3.2\httpclient-4.3.2.jar(org/apache/http/client/config/RequestConfig.class)]]
[正在加载ZipFileIndexFileObject[E:\m2\repository\org\mapu\themis\themis-sdk\1.1.1\themis-sdk-1.1.1.jar(org/mapu/themis/ThemisClient.class)]]
[正在加载ZipFileIndexFileObject[E:\m2\repository\org\springframework\spring-core\3.2.6.RELEASE\spring-core-3.2.6.RELEASE.jar(org/springframework/core/io/Resource.class)]]
[正在加载ZipFileIndexFileObject[E:\m2\repository\org\springframework\spring-core\3.2.6.RELEASE\spring-core-3.2.6.RELEASE.jar(org/springframework/core/io/support/PathMatchingResourcePatternResolver.class)]]
<strong>[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\jre\lib\rt.jar(com/sun/image/codec/jpeg/JPEGCodec.class)]]
[正在加载ZipFileIndexFileObject[D:\Java\jdk1.7.0_40\jre\lib\rt.jar(com/sun/image/codec/jpeg/JPEGImageEncoder.class)]]</strong>
可以看到现在javac使用的rt.jar已经是我们期望的{java_home}/jre/lib/rt.jar了,所以JPEGCodec和JPEGImageEncoder两个类编译通过了,但是jce.jar中的三个类却编译失败,这个时候看了一下类文件搜索路径,恍然大悟,发现少了好几个jar包呀,是因为我修改了bootclasspath造成的默认的bootclasspath被替换掉了,以下是比较:

[类文件的搜索路径: D:\Java\jdk1.7.0_40\jre\lib\resources.jar,D:\Java\jdk1.7.0_40\jre\lib\rt.jar,D:\Java\jdk1.7.0_40\jre\lib\sunrsasign.jar,D:\Java\jdk1.7.0_40\jre\lib\jsse.jar,D:\Java\jdk1.7.0_40\jre\lib\jce.jar,D:\Java\jdk1.7.0_40\jre\lib\charsets.jar,D:\Java\jdk1.7.0_40\jre\lib\jfr.jar,D:\Java\jdk1.7.0_40\jre\classes,D:\Java\jdk1.7.0_40\jre\lib\ext\access-bridge.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\dnsns.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\jaccess.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\localedata.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunec.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunjce_provider.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunmscapi.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunpkcs11.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\zipfs.jar,E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\target\classes,E:\m2\repository\org\mapu\themis\themis-sdk\1.1.1\themis-sdk-1.1.1.jar,E:\m2\repository\com\github\rop\rop-client\2.0\rop-client-2.0.jar,E:\m2\repository\com\github\rop\rop-common\2.0\rop-common-2.0.jar,E:\m2\repository\org\slf4j\slf4j-api\1.7.7\slf4j-api-1.7.7.jar,E:\m2\repository\commons-codec\commons-codec\1.9\commons-codec-1.9.jar,E:\m2\repository\commons-io\commons-io\2.4\commons-io-2.4.jar,E:\m2\repository\org\apache\httpcomponents\httpclient\4.3.2\httpclient-4.3.2.jar,E:\m2\repository\org\apache\httpcomponents\httpcore\4.3.1\httpcore-4.3.1.jar,E:\m2\repository\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar,E:\m2\repository\javax\wsdl\javax.wsdl\1.4.0.v200706111329\javax.wsdl-1.4.0.v200706111329.jar,E:\m2\repository\com\google\guava\guava\15.0\guava-15.0.jar,E:\m2\repository\org\springframework\spring-core\3.2.6.RELEASE\spring-core-3.2.6.RELEASE.jar,.]
[类文件的搜索路径: D:\Java\jdk1.7.0_40\jre\lib\rt.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\access-bridge.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\dnsns.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\jaccess.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\localedata.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunec.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunjce_provider.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunmscapi.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\sunpkcs11.jar,D:\Java\jdk1.7.0_40\jre\lib\ext\zipfs.jar,E:\workspace-sts-3.3.0.RELEASE\ccp_branch\testThemis\target\classes,E:\m2\repository\org\mapu\themis\themis-sdk\1.1.1\themis-sdk-1.1.1.jar,E:\m2\repository\com\github\rop\rop-client\2.0\rop-client-2.0.jar,E:\m2\repository\com\github\rop\rop-common\2.0\rop-common-2.0.jar,E:\m2\repository\org\slf4j\slf4j-api\1.7.7\slf4j-api-1.7.7.jar,E:\m2\repository\commons-codec\commons-codec\1.9\commons-codec-1.9.jar,E:\m2\repository\commons-io\commons-io\2.4\commons-io-2.4.jar,E:\m2\repository\org\apache\httpcomponents\httpclient\4.3.2\httpclient-4.3.2.jar,E:\m2\repository\org\apache\httpcomponents\httpcore\4.3.1\httpcore-4.3.1.jar,E:\m2\repository\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar,E:\m2\repository\javax\wsdl\javax.wsdl\1.4.0.v200706111329\javax.wsdl-1.4.0.v200706111329.jar,E:\m2\repository\com\google\guava\guava\15.0\guava-15.0.jar,E:\m2\repository\org\springframework\spring-core\3.2.6.RELEASE\spring-core-3.2.6.RELEASE.jar,.]
少的那些jar包都是在{java_home}/jre/lib/下面的另外几个jar包,于是我把它们加到了bootclasspath这个参数上于是我的pom文件就变成这样子了:

  <build>
  	<plugins>
  		<plugin>
  			<groupId>org.apache.maven.plugins</groupId>
  			<artifactId>maven-compiler-plugin</artifactId>
  			<version>3.2</version>
  			<configuration>
  			  <compilerArgs>
                             <arg>-verbose</arg>
                             <arg>${java.home}/lib/rt.jar;${java.home}/lib/resources.jar;${java.home}/lib/jsse.jar;${java.home}/lib/jce.jar;${java.home}/l<span style="white-space:pre">				</span>  ib/charsets.jar;${java.home}/lib/jfr.jar</arg>
                         </compilerArgs>
  			</configuration> 
  		</plugin>
  	</plugins>
  </build>
到这里问题就全部解决了。





参考博文:

https://blogs.oracle.com/geertjan/entry/ct_sym_steals_the_asm

https://netbeans.org/bugzilla/show_bug.cgi?id=186120

https://community.oracle.com/thread/1520438?start=0&tstart=0

http://stackoverflow.com/questions/4065401/using-internal-sun-classes-with-javac

http://mail.openjdk.java.net/pipermail/compiler-dev/2013-October/007664.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值