跳下內存通道
早在1998年,當我是一名C / C ++開發人員時,嘗試使用Java時,有關該語言的一些內容對我來說就顯得有些惱火了。 我記得很擔心這些
- 為什么沒有合適的編輯器呢? C / C ++有很多。 我為Java擁有的只是舊的記事本。
- 當我想要的只是一個函數時,為什么我必須上一堂課? 為什么函數也不是對象?
- 為什么我不能將所有內容打包到一個zip / jar中,然后讓最終用戶雙擊啟動?
和其他一些。 那時,我發現自己經常因為無法放棄自己的“ C / C ++思維方式”和擁抱“ Java方式”而自欺欺人。 現在,大約在十年半后的2013年寫這篇文章,令人驚訝的是,所有這些早期的煩惱都消失了。 不是因為我采用了“ Java”方式,而是因為Java發生了變化。
除了閑聊之外,本文的重點是討論以下問題之一:“為什么我不能將所有內容打包到一個zip / jar中,讓最終用戶雙擊啟動?”。
為什么我們需要一個可執行的zip / jar文件?
如果您是開發人員,請在您的IDE上愉快地編碼(我鄙視所有從Eclipse開始使用Java編碼,NetBeans從一開始就不必在記事本上編碼的人),并在Google的幫助下(我非常討厭所有人) (他們不必在Google之前就可以在互聯網上找到東西),可能沒有令人信服的案例。
但是,當您遇到一種情況時
- 您之所以進入數據中心,是因為那里的人遵循了您的部署步驟,但是您的應用程序/網站根本無法正常工作?
- 突然之間,當“根本沒有人碰到”生產箱時,環境變量全部搞砸了,而您就是必須“使其正常工作”的人。
- 您正與業務利益相關者坐在一起,難以置信地盯著“ ClassNotFound例外”,并確信Java根本不喜歡您。
簡而言之,我想說的是,當您處于開發箱/環境的“相對”理智狀態時,一個可執行的jar并不會真正為您做任何事情。 但是,當您進入未知服務器和情況的黃昏區域(沒有IDE和其他工具的情況)時,您就會開始意識到單個可執行jar可以提供多少幫助。
不是嗎
我天真地這么想,并以艱難的方式找到了答案。 讓我一窺。 激發您的編輯人員。 讓我們創建一個可執行的Jar項目。 我使用jdk1.7.0,STS,Maven 3.0.4。 如果您是Maven的新手或剛開始使用,我建議您閱讀
這個和這個 。
檔案:C:\ projects \ MavenCommands.bat
ECHO OFF
REM =============================
REM Set the env. variables.
REM =============================
SET PATH=%PATH%;C:\ProgramFiles\apache-maven-3.0.4\bin;
SET JAVA_HOME=C:\ProgramFiles\Java\jdk1.7.0
REM =============================
REM Standalone java application.
REM =============================
call mvn archetype:generate ^
-DarchetypeArtifactId=maven-archetype-quickstart ^
-DinteractiveMode=false ^
-DgroupId=foo.bar ^
-DartifactId=executableJar001
pause
運行此批處理文件后,您將擁有一個完全可編譯的標準Java應用程序。 繼續編譯它并構建一個jar(mvn -e clean install)。 您將在C:\ projects \ executableJar001 \ target下獲得一個可執行文件Jar001-1.0-SNAPSHOT.jar。 現在放手'
java -jar jarFileName '。 在這里,您第一次跌倒。 在令人討厭的詞匯表中,它告訴您不存在帶有main方法的類,因此它不知道該執行什么。 幸運的是,這很容易。 有標準的Java程序可以解決它。 并且有一個Maven插件可以解決它。 我將使用后者。
更新的文件:/executableJar001/pom.xml
...<dependencies>...</dependencies><build><plugins><!-- Set main class in the jar. --><plugin><groupid>org.apache.maven.plugins</groupid><artifactid>maven-jar-plugin</artifactid><version>2.4</version><configuration><archive><manifest><mainclass>foo.bar.App</mainclass></manifest></archive></configuration></plugin></plugins></build>...
您可以再次編譯和組裝應用程序(mvn -e clean install)。 它會在目標文件夾中創建一個jar文件。 嘗試再次從命令行運行jar 。 這次您將獲得預期的結果。 所以,我們都被排序了,對吧? 錯誤。 錯了
一切似乎都很好。
讓我們深入研究一下,我們會發現為什么現在所有的東西都不像現在看起來那樣排序。 讓我們繼續添加一個依賴,例如,假設我們要添加日志記錄,為此我們要使用第三方jar,即logback。 我將讓Maven處理開發環境中的依賴項。
更新的文件:/executableJar001/pom.xml
...<dependencies><!-- Logging --><dependency><groupid>ch.qos.logback</groupid><artifactid>logback-classic</artifactid><version>1.0.9</version></dependency></dependencies><build>... </build>
更新的文件:/executableJar001/src/main/java/foo/bar/App.java
package foo.bar;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class App
{private final static Logger logger = LoggerFactory.getLogger(App.class);public static void main( String[] args ){System.out.println( 'Hello World!' );logger.debug('Hello world from logger.');}
}
現在,讓我們使用jar命令從命令提示符處編譯并運行jar。 你看到發生了什么事嗎?
Exception in thread 'main' java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
基本上是說找不到LoggerFactory的類(即實際代碼)(即我們在開發環境中添加的第3方jar)。
哦,但是可以肯定的是,我們應該能夠告訴Java從某個文件夾中提取第三方庫。
絕對是 如果您要問這個問題,幾乎可以肯定的是,對于大多數應用程序,您都告訴JVM第三方/依賴庫在哪里。 您可以通過設置classpath來告知。 您可能正在使用某些應用程序服務器,例如Tomcat / Jetty,而這本身可能是一些依賴項。 而這正是問題的根源。
作為開發人員,我提供了一個有效的x.jar。 但是,要使其正常工作,它取決于a.jar(這反過來可能取決于b.jar和c.jar……您明白了)。 當我作為開發人員將我的可交付成果x.jar捆綁在一起時,存在依賴關系-取決于我將其分發給誰-確保在應該使用x.jar的其他環境中正確設置類路徑上班。
多數情況下這沒什么大不了的。 但是,這也不是小事。 有多種方法可以弄亂對目標環境的依賴性。 可能會有例行更新。 在同一生產環境中可能還部署了其他一些應用程序,需要在jar上進行更新,沒人認為會影響您的。 我們可以討論和辯論多種方法來阻止此類不幸事件,但是最重要的是x.jar(開發人員責任)具有依賴性(開發人員不直接控制)。 這會導致不幸。
當然,如果將由于不同版本,不同應用服務器等導致的全部變量添加到此混合中,那么僅提供x.jar的現有解決方案很快就會顯得非常脆弱。
那么我們該怎么辦?
對P. Simon Tuffs博士表示感謝。 這位先生在此環節解釋了他如何迎合這個問題。 這是一本好書,我推薦。 我用非常普通的方式解釋過的(幾乎沒有刮過表面),Simon深入探討了該問題及其解決方法。 長話短說, 他編寫了一個解決方案并將其開源 。 我將不再重播相同的信息-閱讀他的文章 ,內容非常豐富-但我將指出他解決方案的重點。
- 它使人們可以創建一個包含所有內容(包括代碼,資源,依賴項,應用程序服務器(可能))的jar。
- 它允許最終用戶通過簡單的java -jar jarFileName命令來運行整個humongous jar。
- 它允許開發人員以與開發相同的方式進行開發,例如,如果是Web應用程序,war文件結構將保持不變。 因此,在開發過程中沒有任何變化。
那么我們如何去做呢?
在很多地方都有詳細的說明。 One-JAR網站 。 與One-JAR的螞蟻 。 具有一罐的Maven 。
讓我們看看它在我們的偽代碼上的作用。 值得慶幸的是,還有一個Maven插件 。 遺憾的是,它不在Maven Central存儲庫中(為什么?伙計們為什么?您投入了98%的工作。為什么對最后2%的工作比較遲鈍?)。 它帶有不錯的
使用說明 。
更新的文件:/executableJar001/pom.xml
...<dependencies>... </dependencies><build><plugins>...<!-- If you wanted to bundle all this in one jar. --><plugin><groupid>org.dstovall</groupid><artifactid>onejar-maven-plugin</artifactid><version>1.4.4</version><executions><execution><goals><goal>one-jar</goal></goals></execution></executions></plugin></plugins></build><!-- Required only if you are usng onejar plugin. --><pluginrepositories><pluginrepository><id>onejar-maven-plugin.googlecode.com</id><url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url></pluginrepository></pluginrepositories>
現在您需要做的就是運行mvn -e clean package。 除了普通的廣口瓶外,您還會得到一個脂肪充足的廣口瓶。 繼續,再次從命令提示符處執行java -jar jarFileName。 它應該工作。
嗯..聽起來不錯。 為什么每個人都沒有這樣做? 而且這個One-JAR似乎是從2004年開始出現的。為什么我們在這個市場上看不到更多參與者?
你知道他們說免費午餐嗎? 沒有了。 盡管這個概念非常簡潔實用,但這并不意味著其他所有參與者都決定加入。因此,如果您的網站“需要”托管在大型付費應用程序服務器之一上(我不知道為什么您希望繼續為那些了解它們的專有軟件和技術人員付費。如果您不只是為高素質的技術人員付費,而是依賴那些不會讓您陷入困境的開源應用程序,那么One-JAR對您來說可能不是一個可行的解決方案。 此外,我聽到關于事情可能會變慢的不確定聲音(如果您的應用很大,則在加載過程中。因此,在決定使用此功能之前,我建議您進行一次POC并確保您的技術堆棧中沒有其他內容對One-JAR不滿意。
我個人認為,2004年對于這種事情可能還為時過早。 人們仍然在為諸如構建和發布過程的標準化,在ORM領域獲得明確的參與者,為MVC框架尋求明確的參與者等之類的問題而苦苦掙扎。這些問題還沒有得到答案,或者很快就會出現。 但是我認為IT世界中當前問題的味道很普遍
- 如何使DevOps工作。
- 如何使整個構建和發布自動化。
- 如何利用開放源代碼庫提供可靠的軟件,同時確保沒有笨重的專有軟件引起鎖定,從而使解決方案的敏捷性降低,以適應未來的業務需求。
在我看來,One-JAR在該地區表現非常出色。 因此,我當然希望看到更多有關此工具和/或圍繞此概念的更多工具。 而且,公平地說,該領域有更多參與者。 感謝Christian Schlichtherle指出了這一點。 有Maven Assembly Plugin和Maven Shade Plugin可以解決這個完全相同的問題。 我還沒有嘗試過它們,但是從文檔上看,它們很不錯,功能很明智。 Dropwizard ,雖然不是同一回事,但本質上非常相似。 他們通過嵌入式應用程序服務器擴展了整個jar的概念,對REST,JSON,Logback的開箱即用支持,以及一個很好的整潔包,您可以直接使用。
因此,正如我一直在說的那樣,現在是從事技術業務的激動人心的時刻,尤其是如果您喜歡修補軟件。
參考: Tech Jar for Enterprise博客上的Jjar合作伙伴 Partho可以對所有這些規則進行統治 。
翻譯自: https://www.javacodegeeks.com/2013/01/one-jar-to-rule-them-all.html