Bạn đang gặp sự hiểu lầm biên soạn chéo của source
/target
tùy chọn. Sử dụng phiên bản chính trong classpath của bạn (JDK 7 hoặc 8) nhưng muốn biên dịch dựa trên phiên bản nhỏ (6 trong trường hợp của bạn).
Biên soạn sẽ ổn, nhưng bạn sẽ gặp lỗi tại số thời gian chạy (ví dụ: NoClassDefFoundError
hoặc NoSuchMethodError
, có thể cũng là chung chung hơn LinkageError
).
Sử dụng source
/target
, trình biên dịch Java có thể được sử dụng làm trình biên dịch chéo để tạo tệp lớp có thể chạy trên JDK triển khai phiên bản trước của đặc tả Java SE.
Niềm tin chung là sử dụng hai tùy chọn trình biên dịch sẽ là đủ. Tuy nhiên, tùy chọn source
chỉ định dựa vào phiên bản nào chúng tôi đang biên dịch trong khi tùy chọn target
chỉ định phiên bản Java thấp nhất để hỗ trợ.
Trình biên dịch hoạt động trên thế hệ bytecode và source
và target
được sử dụng để tạo ra bytecode tương thích trong quá trình biên dịch chéo. Tuy nhiên, Java API không được trình biên dịch xử lý (chúng được cung cấp như là một phần của quá trình cài đặt JDK, tệp rt.jar
nổi tiếng). Trình biên dịch không có bất kỳ kiến thức nào về API, nó chỉ biên dịch dựa trên rt.jar
hiện tại. Do đó, khi biên dịch với target=1.6
bằng JDK 1.7, trình biên dịch sẽ vẫn trỏ đến JDK 7 rt.jar
.
Vì vậy, làm cách nào để chúng tôi thực sự có một trình biên dịch chéo chính xác?
Since JDK 7, javac prints a warning trong trường hợp source
/target
không kết hợp với tùy chọn bootclasspath
. Tùy chọn bootclasspath
là tùy chọn chính trong các trường hợp này để trỏ đến rt.jar
của phiên bản Java đích mong muốn (do đó, bạn cần phải cài đặt JDK đích trong máy của mình). Như vậy, javac
sẽ biên dịch hiệu quả với API Java tốt.
Nhưng điều đó có thể vẫn chưa đủ!
Không phải tất cả API Java đều đến từ rt.jar
. Các lớp khác được cung cấp bởi thư mục lib\ext
. Tùy chọn thêm javac
được sử dụng cho điều đó, extdirs
. Từ tài liệu chính thức của Oracle
Nếu bạn đang biên dịch chéo (khởi động lớp chống lại lớp khởi động và mở rộng của triển khai nền tảng Java khác), tùy chọn này chỉ định thư mục chứa các lớp mở rộng.
Do đó, thậm chí sử dụng source
/target
và bootclasspath
lựa chọn, chúng tôi vẫn có thể bỏ lỡ một cái gì đó trong cross-biên soạn, cũng như giải thích trong official example mà đi kèm với tài liệu hướng dẫn javac.
Javac Java nền tảng JDK cũng sẽ mặc định biên dịch dựa trên các lớp khởi động của chính nó, vì vậy chúng tôi cần phải yêu cầu javac biên dịch dựa vào các lớp khởi động JDK 1.5 thay thế. Chúng tôi làm điều này với -bootclasspath và -extdirs. Việc không thực hiện điều này có thể cho phép biên dịch dựa vào API nền tảng Java sẽ không có mặt trên máy ảo 1.5 và sẽ thất bại khi chạy.
Nhưng .. có thể vẫn chưa đủ!
Từ Oracle chính thức documentation
Ngay cả khi bootclasspath và -source/-target đều đặt phù hợp cho cross-biên soạn, biên dịch các hợp đồng nội bộ, chẳng hạn như làm thế nào vô danh các lớp bên trong được biên dịch, có thể khác nhau giữa, nói, javac trong JDK 1.4.2 và javac trong JDK 6 chạy với tùy chọn mục tiêu 1.4.
Các giải pháp đề nghị (từ Oracle official tài liệu)
Các cách đáng tin cậy nhất cách để tạo ra các file lớp mà sẽ làm việc trên một JDK nói riêng và sau đó là để biên dịch các file nguồn sử dụng JDK lâu đời nhất của lãi suất. Nói cách đó, bootclasspath phải được thiết lập để biên dịch chéo mạnh mẽ cho một JDK cũ hơn.
Vì vậy, điều này thực sự là không thể?
Spring 4 currently supports Java 6, 7 and 8. Ngay cả khi sử dụng các tính năng và API Java 7 và Java 8. Làm thế nào nó có thể tương thích với Java 7 và 8?
Mùa xuân sử dụng số điện thoại source
/target
và bootclasspath
linh hoạt. Mùa xuân 4 luôn biên dịch với source
/target
thành Java 6 sao cho bytecode vẫn có thể chạy theo JRE 6. Do đó không có tính năng ngôn ngữ Java 7/8 nào được sử dụng: cú pháp giữ mức Java 6.
Nhưng nó cũng sử dụng API Java 7 và Java 8! Do đó, tùy chọn bootclasspath
không được sử dụng. Tùy chọn, Luồng và nhiều API Java 8 khác được sử dụng. Sau đó nó tiêm đậu phụ thuộc vào Java 7 hoặc Java 8 API chỉ khi JRE 7/8 được phát hiện trong thời gian chạy: cách tiếp cận thông minh!
Nhưng Spring đảm bảo khả năng tương thích API như thế nào?
Sử dụng plugin Maven Animal Sniffer.
Plugin này kiểm tra xem ứng dụng của bạn có tương thích với một phiên bản Java được chỉ định hay không. Được gọi là sniffer động vật vì Sun truyền thống đặt tên các phiên bản Java khác nhau sau different animals (Java 4 = Merlin (chim), Java 5 = Tiger, Java 6 = Mustang (ngựa), Java 7 = Dolphin, Java 8 = không có động vật).
Bạn có thể thêm dòng sau vào tập tin POM của bạn:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.14</version>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java16</artifactId>
<version>1.0</version>
</signature>
</configuration>
<executions>
<execution>
<id>ensure-java-1.6-class-library</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Và việc xây dựng sẽ thất bại ngay sau khi bạn sử dụng JDK 7 API trong khi nhu cầu sử dụng chỉ JDK 6 API.
sử dụng này cũng được đề nghị trong chính thức source
/target
page of the maven-compiler-plugin
Note: Chỉ thiết lập các tùy chọn target
không đảm bảo rằng mã của bạn thực sự chạy trên một JRE với phiên bản cụ thể. Cạm bẫy là việc sử dụng không mong muốn các API chỉ tồn tại trong các JRE sau này, điều này sẽ làm cho mã của bạn thất bại trong thời gian chạy với một lỗi liên kết. Để tránh vấn đề này, bạn có thể cấu hình classpath khởi động của trình biên dịch để phù hợp với JRE đích hoặc sử dụng Plugin Sniffer Maven của Động vật để xác minh mã của bạn không sử dụng các API không mong muốn.
Java 9 Cập nhật
Trong Java 9 cơ chế này has been radically changed đến phương pháp sau đây:
javac --release N ...
Sẽ là ngữ nghĩa tương đương với
javac -source N -target N -bootclasspath rtN.jar ...
ưu điểm chính của phương pháp --release N
:
Cập nhật trên Java 9 và Maven
Kể từ phiên bản 3.6.0
, maven-compiler-plugin
cung cấp hỗ trợ cho Java 9 qua release
tùy chọn của nó:
Các -release
luận cho trình biên dịch Java, hỗ trợ từ Java9
Một ví dụ:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<release>8</release>
</configuration>
</plugin>
Remove '' và bạn sẽ thấy các lỗi cao như bầu trời. –
Tunaki
nói chung, nguồn/đích thường bị hiểu lầm, chúng kiểm tra cú pháp, không sử dụng API. bạn nên luôn luôn sử dụng [bootstrap] (http://docs.oracle).com/javase/7/docs/technotes/tools/solaris/javac.html) tùy chọn để đảm bảo biên dịch và nhất quán với phiên bản java mục tiêu của bạn. Ngoài ra, plugin maven động vật sniffer là cách để phá vỡ xây dựng nếu bạn sử dụng java 7/8 API trong khi biên dịch với mục tiêu 6 –
Hơn nữa, bạn nên sử dụng toolchain để xác định chính xác JDK đã sử dụng độc lập với JDK được sử dụng để chạy Maven ... – khmarbaise