2016-03-10 17 views
6

tôi có:Maven Java Version Cấu hình bỏ qua bởi Eclipse/Idea

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

Tuy nhiên, tôi không có vấn đề tuyên bố:

public enum DirectoryWatchService { 

    INSTANCE; 

    private java.util.Optional<String> test; 
    private java.nio.file.Files files; 
} 

Eclipse không bận tâm. IntelliJ cũng vậy. Ngay cả Maven cũng không bận tâm. Tôi thậm chí có thể làm một gói sạch mvn. Xây dựng điều darn mà không có bất kỳ cảnh báo nào.

+0

Remove '' và bạn sẽ thấy các lỗi cao như bầu trời. – Tunaki

+0

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 –

+0

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

Trả lời

10

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à sourcetarget đượ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/targetbootclasspath 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/targetbootclasspath 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 ... 
  • Thông tin về API của các phiên bản trước đó có sẵn để javac

    • lưu trữ trong một thời trang nén
    • Chỉ cung cấp Java SE N và JDK API N -exported đó là nền tảng trung lập
  • Cùng một bộ giá trị phát hành N cho số -source/-target
  • kết hợp không tương thích các tùy chọn từ chối

ưu điểm chính của phương pháp --release N:

  • Không sử dụng cần phải quản lý hiện vật lưu trữ thông tin API cũ
  • nên loại bỏ nhu cầu sử dụng các công cụ như plugin Maven Animal Sniffer
  • Có thể sử dụng thành ngữ biên soạn mới hơn javac trong phiên bản cũ

    • Bug sửa
    • Cải tiến tốc độ

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> 
+0

Cảm ơn bạn đã giải thích chi tiết. Tôi sẽ cung cấp cho các plugin động vật được đề cập một thử. –

+2

Một vài nhận xét nhỏ về câu trả lời này. Tùy chọn '-source' kiểm soát * mức độ ngôn ngữ * của mã nguồn được trình biên dịch chấp nhận. Tùy chọn '-target' điều khiển phiên bản * classfile * được phát ra bởi trình biên dịch. Có vẻ như họ độc lập, nhưng họ thực sự không; sự phụ thuộc có thể khá phức tạp. Ví dụ, biên dịch lambda yêu cầu bytecode 'invokedynamic', do đó,' -source 1.8 -target 1.6' sẽ không hoạt động. –

+1

Sự nhấn mạnh vào các API của thư viện cũng rất quan trọng. Một số tính năng ngôn ngữ nhất định, chẳng hạn như * try-with-resources *, yêu cầu các API thư viện cụ thể như 'AutoCloseable'. Do đó, '-source 1.7 -target 1.6' có thể tạo ra một tệp lớp 1.6 hợp lệ, nhưng nó sẽ không chạy được trên JDK 6. Câu trả lời ở đây là khá chính xác khi nói rằng' -source x -arget x' là không đủ để vượt qua -compile thành phiên bản JDK cũ hơn. Nó rất quan trọng để chú ý đến các vấn đề API thư viện là tốt. –

Các vấn đề liên quan