Gọi “java -jar MyFile.jar” với tùy chọn classpath bổ sung


91

Tôi đã tạo một tệp jar chứa tất cả những thứ đã biên dịch của mình. Ngoài ra, tập lệnh xây dựng kiến ​​của tôi sao chép các lib cần thiết vào một thư mục con "libs". Cấu trúc trông như thế này:

MyProgram.jar
libs/

Vì vậy, khi tôi cố gắng chạy chương trình của mình bây giờ, tôi gặp lỗi sau:

java -cp ".:/home/user/java/MyProgram/jar/libs" -jar MyProgram.jar
java.lang.ClassNotFoundException: org.postgresql.Driver
    at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:186)
    at database.PostgresQL.getConnection(PostgresQL.java:38)
    at recommender.dao.Creative2IdxDAO.createCreatives2Idx(Creative2IdxDAO.java:19)
    at main.Main.calculateCorrelationMatrix(Main.java:51)
    at main.Main.main(Main.java:28)
java.lang.NullPointerException
    at recommender.dao.Creative2IdxDAO.createCreatives2Idx(Creative2IdxDAO.java:25)
    at main.Main.calculateCorrelationMatrix(Main.java:51)
    at main.Main.main(Main.java:28)

Lý do tại sao điều này xảy ra?

Câu trả lời:


151

Bạn sử dụng một trong hai -jar hoặc -cp , bạn không thể kết hợp cả hai. Nếu bạn muốn đặt các JAR bổ sung trên classpath thì bạn nên đặt chúng vào tệp kê khai của JAR chính và sau đó sử dụng java -jarhoặc bạn đặt classpath đầy đủ (bao gồm JAR chính và các phụ thuộc của nó) -cpvà đặt tên cho lớp chính một cách rõ ràng trên dòng lệnh

java -cp 'MyProgram.jar:libs/*' main.Main

(Tôi đang sử dụng dir/*cú pháp yêu cầu javalệnh thêm tất cả .jarcác tệp từ một thư mục cụ thể vào classpath. Lưu ý rằng tệp *phải được bảo vệ khỏi sự mở rộng bởi shell, đó là lý do tại sao tôi sử dụng dấu ngoặc kép.)

Bạn đề cập rằng bạn đang sử dụng Ant vì vậy đối với cách tiếp cận tệp kê khai thay thế, bạn có thể sử dụng <manifestclasspath>nhiệm vụ của ant sau khi sao chép các phụ thuộc nhưng trước khi xây dựng JAR.

<manifestclasspath property="myprogram.manifest.classpath" jarfile="MyProgram.jar">
  <classpath>
    <fileset dir="libs" includes="*.jar" />
  </classpath>
</manifestclasspath>

<jar destfile="MyProgram.jar" basedir="classes">
  <manifest>
    <attribute name="Main-Class" value="main.Main" />
    <attribute name="Class-Path" value="${myprogram.manifest.classpath}" />
  </manifest>
</jar>

Với điều này, java -jar MyProgram.jarsẽ hoạt động chính xác và sẽ bao gồm tất cả các libstệp JAR trên classpath.


Thêm vào ở trên, Hoặc nghĩ đến việc thêm các mục nhập jar được yêu cầu trong tệp MANIFEST.MF.
Himanshu Bhardwaj

@HimanshuBhardwaj thực sự, tôi đã thêm một ví dụ về làm thế nào để làm điều đó bằng<manifestclasspath>
Ian Roberts

Có gì với dấu ':' trong 'MyProgram.jar: libs / *' đây có phải là trình tách biệt không?
Gobliins

Dấu hai chấm @Gobliins là dấu phân cách giữa các mục trong đường dẫn trong hệ điều hành Linux và Mac. Trên Windows, bạn sẽ sử dụng dấu chấm phẩy ( ;) thay vì dấu hai chấm trên Windows được sử dụng cho các ký tự ổ đĩa. OP đã sử dụng dấu hai chấm và dấu gạch chéo trong câu hỏi của họ, cho thấy họ đang sử dụng Linux hoặc Mac.
Ian Roberts

5
Nếu các tùy chọn loại trừ lẫn nhau, dòng lệnh sẽ in cảnh báo khi cả hai được sử dụng. Ai có thời gian cho câu đố này ?!
JJS

22

Khi -jartùy chọn được sử dụng, -cptùy chọn sẽ bị bỏ qua. Cách duy nhất để đặt classpath là sử dụng tệp kê khai trong jar.

Sẽ dễ dàng hơn khi chỉ cần sử dụng -cptùy chọn, thêm tệp jar của bạn vào đó, sau đó gọi lớp chính một cách rõ ràng.

Ngoài ra, giả sử /home/user/java/MyProgram/jar/libsthư mục chứa tệp jar (trái ngược với tệp lớp) thì điều này sẽ không hoạt động. Bạn không thể chỉ định một thư mục chứa tệp jar nhưng phải chỉ định từng tệp jar riêng lẻ trong classpath (bạn nên viết một tập lệnh shell đơn giản để thực hiện việc này cho bạn nếu có một số lượng đáng kể các lọ).


0

Nó là một chút khó khăn. Tập lệnh sau là một nỗ lực để lấy classpath từ tệp kê khai của jar và sau đó cho phép thêm các mục nhập classpath bổ sung. Tôi đã có kết quả hỗn hợp với điều này nhưng vẫn muốn chia sẻ tập lệnh để nó có thể được thực hiện đầy đủ chức năng ở đây.

Tập lệnh có hai tên

  • showmanifest
  • calljar

bằng cách liên kết cứng hai tệp với nhau với

ln calljar showmanifest

với calljar -h bạn có thể thấy cách sử dụng.

#!/bin/bash
#set -x
# show the manifest of a jar file
# 2012-07-18
# author WF

#
# show usage
#
usage() {
 echo "usage: showmanifest (jarfile | directory jarfile) " 1>&2
 echo "usage: calljar directory jarfile classpath pattern arguments" 1>&2
 echo "             -h|--help " 1>&2
 echo "               show this help and exit" 1>&2
 echo "             -m|--mainclass javaclass" 1>&2
 echo "               mainclass to use (otherwise manifest is inspected)" 1>&2
 exit 1
}

#
# show the manifest of the given jar file
#
show() {
  dir="$1"
  jar="$2"
    fulljar=`find "$dir" -name "$jar"`
    cd /tmp
    mkdir show$$
    cd show$$
    jar xvf $fulljar META-INF/MANIFEST.MF
    cat META-INF/MANIFEST.MF
    cd /tmp
    rm -rf show$$
}

#
# show the classpath of the manifest
#
calljar() {
  dir="$1"
    jar="$2"
    classpath="$3"
    pattern="$4"
    arguments="$5"
    cmd=`show "$dir" "$jar"   | awk -v extracp="$classpath" -v dir="$dir" -v pattern="$pattern" -v jar="$jar" -v mainclass="$mainclass" -v args="$arguments" '
/Main-Class: / { if (mainclass=="") mainclass=$2 }
/^Class-Path:/ { 
  incp=1; 
    cp=$0; 
    gsub("Class-Path: ","",cp) 
    next
}
/^ .*$/ && incp { 
    line=substr($0,2)
  # remove carriage return (if any)
  cp=cp line
}
END { 
  # we do not like carriage returns
  gsub("\\r","",cp)
  gsub("\\r","",mainclass)
    # we do not like blanks ...
  gsub(" ","",cp)
    gsub(pattern,":"dir"/"pattern,cp)
  print "java -cp " extracp cp ":"dir"/"jar " " mainclass " " args
}
    '`
  #echo $cmd
    $cmd
}


# echo $# arguments found: $*
# parse command line options
while true; do
# echo "option $1"
  case "$1" in
    # options without arguments
    -h|--help) usage;;
         # for options with required arguments, an additional shift is required
        -m|--mainclass) mainclass=$2; shift;;
      (--) shift; break;;
      (-*) echo "$0: error - unrecognized option $1" 1>&2; usage;;
    (*) dir=$1;shift;break;;
  esac
  shift
done

#echo "argcount=$#"
case  $# in
  0) dir=`dirname "$dir"`
       jar=`basename "$dir"`
         show "$dir" "$jar";;
  1) jar="$1"
         show "$dir" "$jar";;
  2) usage;;
    3) usage;;
  *) jar="$1"; shift;
         classpath="$1"; shift;
         pattern="$1"; shift;
         arguments="$@";
    #echo "mainclass=${mainclass}"
    #echo "classpath=${classpath}"

  #echo calljar "${dir}" "${jar}" "${classpath}" "$pattern" "$arguments"
    calljar "$dir" "$jar" "$classpath" "$pattern" "$arguments"
    ;;
esac

-1

Để kiểm tra nhanh, một lần ứng dụng, bạn có thể chỉ cần liên kết biểu tượng các tệp JAR phụ thuộc cần thiết vào thư mục chứa tệp JAR của ứng dụng chính.

Ví dụ (đối với một ứng dụng app.jarsử dụng thư viện Eclipse SWT, mà trong trường hợp của tôi đã được cài đặt /usr/share/java):

$ ln -s /usr/share/java/swt.jar .
$ java -jar app.jar
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.