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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章