Làm cách nào để đặt tất cả các tệp JAR cần thiết trong một thư mục thư viện bên trong tệp JAR cuối cùng với Maven?


104

Tôi đang sử dụng Maven trong ứng dụng độc lập của mình và tôi muốn đóng gói tất cả các phụ thuộc trong tệp JAR của mình bên trong một thư mục thư viện, như đã đề cập trong một trong các câu trả lời ở đây:

Làm cách nào tôi có thể tạo một JAR thực thi với các phụ thuộc bằng Maven?

Tôi muốn tệp JAR cuối cùng của mình có một thư mục thư viện chứa các phần phụ thuộc dưới dạng tệp JAR, không giống như những gì maven-shade-pluginđặt các phần phụ thuộc ở dạng thư mục như hệ thống phân cấp Maven trong thư mục .m2.

Thực sự thì cấu hình hiện tại làm được những gì tôi muốn, nhưng tôi đang gặp sự cố khi tải các tệp JAR khi chạy ứng dụng. Tôi không thể tải các lớp học.

Đây là cấu hình của tôi:

<plugins>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
            <execution>
                <id>copy-dependencies</id>
                <phase>prepare-package</phase>
                <goals>
                    <goal>copy-dependencies</goal>
                </goals>
                <configuration>
                    <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
                    <overWriteReleases>false</overWriteReleases>
                    <overWriteSnapshots>false</overWriteSnapshots>
                    <overWriteIfNewer>true</overWriteIfNewer>
                </configuration>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
            <archive>
                <manifest>
                    <addClasspath>true</addClasspath>
                    <classpathPrefix>lib/</classpathPrefix>
                    <mainClass>com.myapp.MainClass</mainClass>
                </manifest>
            </archive>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <source>1.6</source>
            <target>1.6</target>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
            <execution>
                <id>install</id>
                <phase>install</phase>
                <goals>
                    <goal>sources</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.5</version>
        <configuration>
            <encoding>UTF-8</encoding>
        </configuration>
    </plugin>

</plugins>

Dự án chạy tốt từ Eclipse và các tệp JAR được đặt trong thư mục thư viện bên trong tệp JAR cuối cùng của tôi như tôi muốn, nhưng khi chạy tệp JAR cuối cùng từ thư mục đích, tôi luôn nhận được ClassNotFoundException:

Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContext
Caused by: java.lang.ClassNotFoundException: org.springframework.context.ApplicationContext
        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)
Could not find the main class: com.myapp.MainClass. Program will exit.

Làm cách nào để khắc phục ngoại lệ này?


1
bạn sử dụng lệnh nào để chạy jar? có lẽ bạn có thể thích plugin thực thi maven?
Andrey Borisov

Thông báo ngoại lệ có lỗi thời so với tệp POM không? Có vẻ như lớp chính com.myapp.MainClassđang được tìm kiếm, không phải com.tastycafe.MainClass.
Duncan Jones

@Duncan Jones, vấn đề sao chép dán, tôi đã chỉnh sửa câu hỏi
Mahmoud Saleh

3
Lưu ý rằng nếu bạn muốn các bình bên trong bình, thì bộ nạp lớp chuẩn trong Java không thể hiểu chúng.
Thorbjørn Ravn Andersen

Làm thế nào để làm cho nó đặt các phụ thuộc maven trong một thư mục lib nhưng bên ngoài JAR?
ed22

Câu trả lời:


81

Sau đây là giải pháp của tôi. Kiểm tra nó nếu nó phù hợp với bạn:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
                <overWriteReleases>false</overWriteReleases>
                <overWriteSnapshots>false</overWriteSnapshots>
                <overWriteIfNewer>true</overWriteIfNewer>
            </configuration>
        </execution>
    </executions>
</plugin>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <!-- <classpathPrefix>lib</classpathPrefix> -->
                <!-- <mainClass>test.org.Cliente</mainClass> -->
            </manifest>
            <manifestEntries>
                <Class-Path>lib/</Class-Path>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

Plugin đầu tiên đặt tất cả các phụ thuộc vào thư mục target / class / lib và plugin thứ hai bao gồm thư mục thư viện trong tệp JAR cuối cùng và định cấu hình Manifest.mftệp.

Nhưng sau đó, bạn sẽ cần thêm mã tải lớp tùy chỉnh để tải các tệp JAR.

Hoặc, để tránh tải lớp tùy chỉnh, bạn có thể sử dụng "$ {project.build.directory} / lib, nhưng trong trường hợp này, bạn không có phần phụ thuộc bên trong tệp JAR cuối cùng, điều này không phù hợp với mục đích.

Đã hai năm kể từ khi câu hỏi được đặt ra. Tuy nhiên, vấn đề của các tệp JAR lồng nhau vẫn còn. Tôi hy vọng nó sẽ giúp ai đó.


1
Cách sử dụng: mvn install, cd target, java -jar MyJarFile-1.0.jar
djb

tôi nhớ gì Điều này tạo ra một mục nhập classpath tệp kê khai có chứa "lib /" và tất cả các jar đơn từ thư mục lib. Đây là dự định? Tại sao?
gapvision

2
Nó hoạt động sau khi tôi thay đổi "$ {project.build.directory} / Class / lib" thành $ {project.build.directory} / lib
Rajendra Thorat

1
Không may bao gồm các phụ thuộc được khai báo như được cung cấp.
Philippe Gioseffi

@Rajendra cảm ơn, điều đó đã giúp: $ {project.build.directory} / lib
Mindaugas K.

30

Đã cập nhật:

<build> 
  <plugins> 
    <plugin> 
    <artifactId>maven-dependency-plugin</artifactId> 
    <executions> 
      <execution> 
        <phase>install</phase> 
          <goals> 
            <goal>copy-dependencies</goal> 
          </goals> 
          <configuration> 
             <outputDirectory>${project.build.directory}/lib</outputDirectory> 
          </configuration> 
        </execution> 
      </executions> 
    </plugin> 
  </plugins> 
</build> 

4
điều này đặt thư mục lib bên ngoài jar (đó không phải là những gì tôi muốn). Điều đó có tốt hơn việc đặt lib vào trong lọ không? và làm thế nào để giao ứng dụng cho khách hàng trong trường hợp này?
Mahmoud Saleh

nó rõ ràng hơn cho tôi bây giờ. hãy để tôi thực hiện một tìm kiếm trên google. Nhưng tôi muốn biết tại sao bạn muốn sao chép tất cả tệp jar phụ thuộc trong thư mục được chỉ định bên trong tệp jar thực thi. Nếu tất cả các tệp jar phụ thuộc nằm trong tệp jar, tại sao bạn cần định vị chúng trong thư mục lib?
Ahmet Karakaya

23

Cách đơn giản và hiệu quả nhất là sử dụng một plugin uber như sau:

          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <finalName>uber-${artifactId}-${version}</finalName>
            </configuration>
        </plugin>

Bạn sẽ khử chuẩn hóa tất cả trong một tệp JAR.


sử dụng nó ngoài cấu hình hiện tại của tôi? và chính xác thì plugin này làm gì?
Mahmoud Saleh

15
tôi không muốn tệp jar của mình trông như thế này, tôi muốn có tất cả các phụ thuộc trong một thư mục lib bên trong tệp jar như netbean.
Mahmoud Saleh

12

Các nhà đóng gói thực thi maven Plugin có thể được sử dụng cho mục đích chính xác điều đó: tạo các ứng dụng độc lập java chứa tất cả phụ thuộc như các tập tin JAR trong một thư mục cụ thể.

Chỉ cần thêm phần sau vào pom.xmlbên trong <build><plugins>phần của bạn (hãy đảm bảo thay thế giá trị của mainClasstương ứng):

<plugin>
    <groupId>de.ntcomputer</groupId>
    <artifactId>executable-packer-maven-plugin</artifactId>
    <version>1.0.1</version>
    <configuration>
        <mainClass>com.example.MyMainClass</mainClass>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>pack-executable-jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Tệp JAR đã xây dựng được đặt tại target/<YourProjectAndVersion>-pkg.jarsau khi bạn chạy mvn package. Tất cả các phụ thuộc thời gian biên dịch và thời gian chạy của nó sẽ được đưa vào lib/thư mục bên trong tệp JAR.

Tuyên bố từ chối trách nhiệm: Tôi là tác giả của plugin.


Tôi đã thử và tôi có thể nói rằng nó đang hoạt động ... Như bạn đã nói, plugin này tạo một tệp jar, với các thư viện (lọ) của nó vào một thư mục lib trong jar ... Cấu hình thực sự dễ dàng .. Tôi nghĩ đó là những gì tác giả của câu hỏi này đang mong đợi ... Tôi sẽ cho bạn phiếu bầu của tôi ... Hạn chế duy nhất mà tôi đã tìm thấy trên nó (đó là lý do tại sao tôi không thể sử dụng nó), có sự chậm trễ khi tôi thực hiện jar và ứng dụng được hiển thị (khoảng 20 giây) có thể là do quá trình đăng ký các libs trong trình tải lớp tùy chỉnh ... Nhưng nó là một cách tiếp cận tuyệt vời và một plugin tuyệt vời ...
Adam M. Gamboa G.


3

Đây là cách tôi làm điều đó:

    <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <appendAssemblyId>false</appendAssemblyId>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>com.project.MainClass</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>

Và sau đó tôi chỉ chạy:

mvn assembly:assembly

1
Lưu ý rằng bạn nên luôn thực hiện biên dịch trước vì assemblysẽ chỉ đặt bất cứ thứ gì có trong "target / class" trong JAR. Điều này sẽ đảm bảo rằng JAR bao gồm mọi thay đổi mà bạn đã thực hiện gần đây đối với mã nguồn. Vì vậy, bạn nên làm một cái gì đó như: mvn clean compile assembly:assembly.
naXa

2

Tôi đã tìm thấy câu trả lời này cho câu hỏi:

http://padcom13.blogspot.co.uk/2011/10/creating-standalone-application-with.html

Bạn không chỉ nhận được các tệp lib phụ thuộc trong thư mục lib, bạn còn nhận được trình giám đốc bin với cả tệp thực thi unix và dos.

Cuối cùng tệp thực thi gọi java với đối số -cp cũng liệt kê tất cả các lib phụ thuộc của bạn.

Toàn bộ lô nằm trong một thư mục lắp ráp bên trong thư mục đích. Sử thi.

============= Vâng, tôi biết đây là một chủ đề cũ, nhưng nó vẫn có vị trí cao trên kết quả tìm kiếm nên tôi nghĩ nó có thể giúp ích cho những người như tôi.


1

Đây rõ ràng là một vấn đề classpath. Hãy lưu ý rằng classpath phải thay đổi một chút khi bạn chạy chương trình của mình bên ngoài IDE. Điều này là do IDE tải các JAR khác liên quan đến thư mục gốc của dự án của bạn, trong khi trong trường hợp JAR cuối cùng, điều này thường không đúng.

Điều tôi muốn làm trong những tình huống này là xây dựng JAR theo cách thủ công. Tôi mất nhiều nhất là 5 phút và nó luôn giải quyết được vấn đề. Tôi không đề nghị bạn làm điều này. Tìm cách sử dụng Maven, đó là mục đích của nó.


@SoboLAN Xây dựng JAR theo cách thủ công không phải là giải pháp ở đây. Mục đích là sử dụng Maven, hoàn toàn ngược lại với "thủ công"!
Duncan Jones

@DuncanJones Bạn hoàn toàn đúng. Tôi đề nghị anh ấy sử dụng Maven để làm điều đó. Tuy nhiên, tôi không có kinh nghiệm với nó và không biết chính xác giải pháp nào để đề xuất. Tôi đã chỉnh sửa câu trả lời của mình để phản ánh điều này.
Radu Murzea
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.