Sự khác nhau giữa phụ thuộcQuản lý và phụ thuộc trong Maven


766

Sự khác biệt giữa dependencyManagementvà là dependenciesgì? Tôi đã thấy các tài liệu tại trang web Apache Maven. Dường như một phụ thuộc được xác định theo dependencyManagementcó thể được sử dụng trong các mô đun con của nó mà không chỉ định phiên bản.

Ví dụ:

Dự án mẹ (Pro-par) xác định một phụ thuộc theo dependencyManagement:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8</version>
    </dependency>
 </dependencies>
</dependencyManagement>

Sau đó, trong đứa trẻ của Pro-par, tôi có thể sử dụng Junit:

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
    </dependency>
 </dependencies>

Tuy nhiên, tôi tự hỏi nếu nó là cần thiết để xác định Junit trong pom cha? Tại sao không xác định nó trực tiếp trong các mô-đun cần thiết?

Câu trả lời:


464

Quản lý phụ thuộc cho phép hợp nhất và tập trung quản lý các phiên bản phụ thuộc mà không cần thêm phụ thuộc được thừa kế bởi tất cả trẻ em. Điều này đặc biệt hữu ích khi bạn có một tập hợp các dự án (tức là nhiều hơn một) kế thừa cha mẹ chung.

Một trường hợp sử dụng cực kỳ quan trọng khác dependencyManagementlà việc kiểm soát các phiên bản của tạo tác được sử dụng trong các phụ thuộc bắc cầu. Điều này thật khó để giải thích nếu không có ví dụ. May mắn thay, điều này được minh họa trong tài liệu.


17
Vì vậy, dù sao cũng cần phải khai báo các phụ thuộc trong pom của dự án con ngay cả khi chúng đã khai báo trong pom của dự án mẹ tại phần <DepencyMan Management>? Có thể thực hiện một số loại thừa kế của phụ thuộc?
johnny-b-goode

55
Có, bạn vẫn cần xác định chúng trong POM con để cho thấy rằng bạn đang sử dụng chúng. Chúng không thực sự được bao gồm trong các dự án con chỉ vì chúng nằm trong <dependencyManagement>POM gốc. Bao gồm các phụ thuộc trong <dependencyManagement>việc quản lý tập trung phiên bản, phạm vi và loại trừ cho từng phụ thuộc, nếu và khi bạn quyết định sử dụng nó. Hướng dẫn của Maven về quản lý phụ thuộc được đưa vào tất cả các chi tiết.
hotshot309

2
Đoạn thứ hai ( dependencyManagementcũng kiểm soát các phụ thuộc bắc cầu) chỉ đúng khi các phụ thuộc được đặt rõ ràng: stackoverflow.com/questions/28312975/ Kẻ
Robert Metzger

2
@ johnny-b-goode Những gì bạn vẫn có thể làm là tạo một dependenciesphần mới trong pom cha mẹ của bạn. Chúng tôi đã làm điều đó để tất cả các dự án con có một số apache-commons theo mặc định và không khai báo chúng mọi lúc.
рüффп

771

Tôi đến muộn với câu hỏi này, nhưng tôi nghĩ rằng nó đáng để trả lời rõ ràng hơn câu hỏi được chấp nhận (điều này đúng, nhưng không nhấn mạnh phần quan trọng thực sự mà bạn cần phải tự suy luận).

Trong POM gốc, sự khác biệt chính giữa <dependencies><dependencyManagement>là:

Các tạo phẩm được chỉ định trong <dependencies>phần sẽ LUÔN LUÔN được đưa vào như một phần phụ thuộc của (các) mô đun con.

Các tạo phẩm được chỉ định trong <dependencyManagement>phần, sẽ chỉ được bao gồm trong mô đun con nếu chúng cũng được chỉ định trong <dependencies>phần của chính mô đun con. Tại sao nó tốt bạn hỏi? bởi vì bạn chỉ định phiên bản và / hoặc phạm vi trong cha mẹ và bạn có thể loại bỏ chúng khi chỉ định các phụ thuộc trong POM con. Điều này có thể giúp bạn sử dụng các phiên bản hợp nhất cho các phụ thuộc cho các mô đun con, mà không chỉ định phiên bản trong mỗi mô đun con.


1
Nhưng không phải là nó cũng có một chút của một overhead, sử dụng <dependencyManagement>trên <dependencies>trong thư mục gốc .pom? Trẻ em pomcó thể ngắn hơn nhiều.
Janez Kuhar

18
Đúng. Sử dụng <phụ thuộc> thay vì <phụ thuộc quản lý> sẽ tạo ra các poms con ngắn hơn. Tuy nhiên, nó đi kèm với một chi phí - điều đó có nghĩa là những phụ thuộc đó sẽ LUÔN được xác định cho TẤT CẢ các mô-đun con. Nếu chỉ MỘT SỐ mô-đun con cần một sự phụ thuộc nhất định, thay vì sử dụng "<phụ thuộc quản lý>" thay vào đó sẽ cho phép bạn chọn mô-đun con nào sẽ có sự phụ thuộc đó và vẫn hiệu quả một chút bằng cách chỉ đặt phiên bản phụ thuộc trong pom cha.
dcoder

2
@JanezKuhar Điều đó có ý nghĩa với tôi rằng nếu bạn chỉ định một phụ thuộc trong mô đun con, nó sẽ ghi đè lên phụ huynh, nhưng tôi thừa nhận tôi không nhớ. Tôi sẽ phải kiểm tra tài liệu maven cho tài liệu đó khi tôi có cơ hội. Mặc dù có thể dễ dàng hơn khi chỉ thiết lập một dự án cha-con đơn giản và xác minh :)
dcoder

26
Giải thích tốt cho một khái niệm đơn giản - tại sao dường như rất khó để Maven giải thích công cụ của riêng họ một cách dễ dàng?
jimmy_terra

1
Tôi sẽ thêm vào Artifacts specified in the <dependencies> section will ALWAYS be included as a dependency of the child module(s)rằng họ cũng được bao gồm trong cha mẹ. Có vẻ như không thể thiết lập một sự phụ thuộc cho trẻ em, nhưng không phải cho cha mẹ.
caduceus

54

Các tài liệu trên trang web Maven là khủng khiếp. Những gì phụ thuộc Quản lý làm chỉ đơn giản là di chuyển các định nghĩa phụ thuộc của bạn (phiên bản, loại trừ, v.v.) lên pom cha, sau đó trong các pom con bạn chỉ cần đặt groupId và artifactId. Đó là nó (ngoại trừ chuỗi pom cha mẹ và tương tự, nhưng điều đó cũng không thực sự phức tạp - Sự phụ thuộc Quản lý chiến thắng các phụ thuộc ở cấp độ cha mẹ - nhưng nếu có câu hỏi về điều đó hoặc nhập khẩu, tài liệu Maven tốt hơn một chút).

Sau khi đọc tất cả rác 'a', 'b', 'c' trên trang Maven và bị lẫn lộn, tôi đã viết lại ví dụ của họ. Vì vậy, nếu bạn có 2 dự án (proj1 và proj2) có chung một phụ thuộc chung (betaShared), bạn có thể chuyển phụ thuộc đó lên pom cha. Trong khi bạn đang ở đó, bạn cũng có thể chuyển lên bất kỳ phụ thuộc nào khác (alpha và charlie) nhưng chỉ khi nó có ý nghĩa cho dự án của bạn. Vì vậy, đối với tình huống được nêu trong các câu trước, đây là giải pháp với sự phụ thuộc Quản lý trong pom cha:

<!-- ParentProj pom -->
<project>
  <dependencyManagement>
    <dependencies>
      <dependency> <!-- not much benefit defining alpha here, as we only use in 1 child, so optional -->
        <groupId>alpha</groupId>
        <artifactId>alpha</artifactId>
        <version>1.0</version>
        <exclusions>
          <exclusion>
            <groupId>zebra</groupId>
            <artifactId>zebra</artifactId>
          </exclusion>
        </exclusions>
      </dependency>
      <dependency>
        <groupId>charlie</groupId> <!-- not much benefit defining charlie here, so optional -->
        <artifactId>charlie</artifactId>
        <version>1.0</version>
        <type>war</type>
        <scope>runtime</scope>
      </dependency>
      <dependency> <!-- defining betaShared here makes a lot of sense -->
        <groupId>betaShared</groupId>
        <artifactId>betaShared</artifactId>
        <version>1.0</version>
        <type>bar</type>
        <scope>runtime</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

<!-- Child Proj1 pom -->
<project>
  <dependencies>
    <dependency>
      <groupId>alpha</groupId>
      <artifactId>alpha</artifactId>  <!-- jar type IS DEFAULT, so no need to specify in child projects -->
    </dependency>
    <dependency>
      <groupId>betaShared</groupId>
      <artifactId>betaShared</artifactId>
      <type>bar</type> <!-- This is not a jar dependency, so we must specify type. -->
    </dependency>
  </dependencies>
</project>

<!-- Child Proj2 -->
<project>
  <dependencies>
    <dependency>
      <groupId>charlie</groupId>
      <artifactId>charlie</artifactId>
      <type>war</type> <!-- This is not a jar dependency, so we must specify type. -->
    </dependency>
    <dependency>
      <groupId>betaShared</groupId> 
      <artifactId>betaShared</artifactId> 
      <type>bar</type> <!-- This is not a jar dependency, so we must specify type. -->
    </dependency>
  </dependencies>
</project>

2
Một số câu hỏi ngoài chủ đề: loại "thanh" phụ thuộc nghĩa là gì? Tôi đã thấy trong một ví dụ về tài liệu Maven nhưng không thể tìm thấy định nghĩa. Tôi cho rằng đó là một lỗi đánh máy của "chiến tranh" hoặc "bình", nhưng tôi thấy nó trong các ví dụ khác như của bạn.
NobodyMan

NobodyMan - Vì vậy, nó chỉ là một trình giữ chỗ cho một loại lưu trữ khác. Giống như sử dụng 'foo'. Hoặc nó có thể được sử dụng nếu ai đó tạo một loại tùy chỉnh với 'thanh' tiện ích mở rộng. Và có rất nhiều loại lưu trữ tối nghĩa ngoài kia. Giống như sar, đó là lưu trữ dịch vụ jboss.
MattC

Ví dụ của bạn khá rõ ràng và khẳng định lại những gì tôi đã tự mò mẫm từ tài liệu. Bạn đã gửi nó cho dự án Maven? Sau khi nghiên cứu ví dụ của bạn, tôi đang chuẩn bị đơn giản hóa một POM có cả hai và chỉ cần các khai báo phụ thuộc, vì dự án mà nó được liên kết không có con.
David A. Gray

Chà, tôi chuẩn bị bỏ nút Dependency Manager, cho đến khi tôi nhận ra rằng việc để nó cho phép tôi thiết lập một phiên bản tối thiểu cho bất kỳ POM con nào tìm đường vào cây phụ thuộc một cách gián tiếp. Ví dụ, khi truy tìm javax.cache.cache-apI, tôi đã phát hiện ra một phiên bản 1.0.0 mới hơn đáng kể (so với 0.3.0) cũng có thể được sử dụng xuyên suốt.
David A. Gray

Giải thích này là pefect.
thông minh

45

Giống như bạn đã nói; dependencyManagementđược sử dụng để kéo tất cả thông tin phụ thuộc vào tệp POM chung, đơn giản hóa các tham chiếu trong tệp POM con.

Nó trở nên hữu ích khi bạn có nhiều thuộc tính mà bạn không muốn nhập lại trong nhiều dự án con.

Cuối cùng, dependencyManagementcó thể được sử dụng để xác định một phiên bản tiêu chuẩn của một vật phẩm để sử dụng trên nhiều dự án.


4
Vì vậy, phụ thuộc không được thừa kế? Nó cần phải được tuyên bố trong pom dự án con?
johnny-b-goode

6
Có, bạn cần phải khai báo chúng trong các dự án trẻ em, nhưng không chỉ định một phiên bản.
Pavel Vlasov

Kịch bản này hữu ích khi bạn muốn quản trị các phiên bản trong nhiều dự án java có mối quan hệ cha-con.
Anuj Kumar

43

Theo tôi, vẫn còn một điều chưa được làm nổi bật đủ và đó là sự kế thừa không mong muốn .

Đây là một ví dụ gia tăng:

Tôi tuyên bố trong parentpom của tôi :

<dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>
</dependencies>

bùng nổ! Tôi có nó trong Child A, Child BChild Ccác mô-đun:

  • Implicilty được thừa kế bởi poms trẻ em
  • Một nơi duy nhất để quản lý
  • Không cần phải kê khai lại bất cứ thứ gì trong poms trẻ em
  • Tôi vẫn có thể redelcare và ghi đè vào version 18.0trong một Child Bnếu tôi muốn.

Nhưng điều gì sẽ xảy ra nếu tôi cuối cùng không cần ổi Child C, và trong tương lai Child DChild Ecác mô-đun?

Họ vẫn sẽ thừa hưởng nó và điều này là không mong muốn! Điều này giống như mùi mã Java Object Object, nơi bạn thừa hưởng một số bit hữu ích từ một lớp và cũng có rất nhiều thứ không mong muốn.

Đây là nơi <dependencyManagement>đi vào chơi. Khi bạn thêm nó vào pom cha mẹ của bạn, tất cả các mô-đun con của bạn DỪNG nhìn thấy nó . Và do đó, bạn buộc phải đi vào từng mô-đun riêng lẻ mà KHÔNG cần nó và khai báo lại ( Child AChild B, mặc dù không có phiên bản).

Và, rõ ràng, bạn không làm điều đó cho Child C, và do đó mô-đun của bạn vẫn còn nạc.


Các phần phụ thuộc được đề cập trong <phụ thuộc quản lý> có bị giảm cho dự án pom cha không?
Jaspreet Jolly

Bạn có chắc chắn rằng nếu chúng ta sử dụng <dependencyManagement>trong pom cha thì mặc định phụ thuộc sẽ không được thừa kế trong các pom con? Bởi vì trong tài liệu: maven.apache.org/guides/int sinhtion / Đạo trong khi giải thích việc sử dụng thứ hai của <dependencyManagement>nó, có vẻ như nó sẽ được kế thừa theo mặc định. Tại một dòng họ nói rằng: "Khi maven chạy trên dự án B, phiên bản 1.0 của các tạo phẩm a, b, c và d sẽ được sử dụng bất kể phiên bản được chỉ định trong pom của họ" mặc dù "b" không được sử dụng trong dự án B
chirag soni

Hãy tự mình thử
Andrejs

17

Có một vài câu trả lời nêu rõ sự khác biệt giữa <depedencies><dependencyManagement>thẻ với maven.

Tuy nhiên, một vài điểm được xây dựng dưới đây một cách súc tích:

  1. <dependencyManagement>cho phép hợp nhất tất cả các phụ thuộc (được sử dụng ở cấp độ pom con) được sử dụng trên các mô-đun khác nhau - sự rõ ràng , quản lý phiên bản phụ thuộc trung tâm
  2. <dependencyManagement>cho phép dễ dàng nâng cấp / hạ cấp các phụ thuộc dựa trên nhu cầu, trong trường hợp khác, điều này cần được thực hiện ở mọi cấp độ trẻ em - tính nhất quán
  3. các phụ thuộc được cung cấp trong <dependencies>thẻ luôn được nhập, trong khi các phụ thuộc được cung cấp tại <dependencyManagement>cha mẹ sẽ chỉ được nhập nếu pom con có mục tương ứng trong <dependencies>thẻ.

17

Xin lỗi tôi đến bữa tiệc rất muộn.

Hãy để tôi cố gắng giải thích sự khác biệt bằng cách sử dụng mvn dependency:treelệnh

Hãy xem xét ví dụ dưới đây

Phụ huynh POM - Dự án của tôi

<modules>
    <module>app</module>
    <module>data</module>
</modules>

<dependencies>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>19.0</version>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
    </dependencies>
</dependencyManagement>

POM con - mô-đun dữ liệu

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
</dependencies>

POM con - mô-đun ứng dụng (không có phụ thuộc thêm, do đó để trống phụ thuộc)

 <dependencies>
</dependencies>

Khi chạy mvn dependency:treelệnh, chúng tôi nhận được kết quả sau

Scanning for projects...
------------------------------------------------------------------------
Reactor Build Order:

MyProject
app
data

------------------------------------------------------------------------
Building MyProject 1.0-SNAPSHOT
------------------------------------------------------------------------

--- maven-dependency-plugin:2.8:tree (default-cli) @ MyProject ---
com.iamvickyav:MyProject:pom:1.0-SNAPSHOT
\- com.google.guava:guava:jar:19.0:compile

------------------------------------------------------------------------
Building app 1.0-SNAPSHOT
------------------------------------------------------------------------

--- maven-dependency-plugin:2.8:tree (default-cli) @ app ---
com.iamvickyav:app:jar:1.0-SNAPSHOT
\- com.google.guava:guava:jar:19.0:compile

------------------------------------------------------------------------
Building data 1.0-SNAPSHOT
------------------------------------------------------------------------

--- maven-dependency-plugin:2.8:tree (default-cli) @ data ---
com.iamvickyav:data:jar:1.0-SNAPSHOT
+- org.apache.commons:commons-lang3:jar:3.9:compile
\- com.google.guava:guava:jar:19.0:compile

Quả ổi của Google được liệt kê là phụ thuộc trong mọi mô-đun (bao gồm cả cha mẹ), trong khi dấu phẩy apache được liệt kê là phụ thuộc chỉ trong mô-đun dữ liệu (thậm chí không có trong mô-đun mẹ)


11

Nếu phần phụ thuộc được xác định trong phần tử Quản lý phụ thuộc của pom cấp cao nhất, dự án con không phải liệt kê rõ ràng phiên bản của phần phụ thuộc. nếu dự án con đã xác định một phiên bản, nó sẽ ghi đè lên phiên bản được liệt kê trong phần Quản lý phụ thuộc của POM cấp cao nhất. Đó là, phiên bản quản lý phụ thuộc chỉ được sử dụng khi đứa trẻ không khai báo trực tiếp một phiên bản.


1
Tôi tin rằng tuyên bố này có thể không chính xác. Trong các ví dụ về Quản lý phụ thuộc của Maven (# 2), họ nói rằng các phụ thuộc được xác định trong pom cha với một phiên bản, sẽ ghi đè lên phiên bản được chỉ định trong pom con: "Khi maven chạy trên dự án B phiên bản 1.0 của tạo tác a, b, c và d sẽ được sử dụng bất kể phiên bản được chỉ định trong pom của họ. "
devdanke

@devdanke Ít nhất, các vấn đề M2E Eclipse một cảnh báo: trọng quản lý phiên bản ... cho ... .
GeroldBroser phục hồi Monica

4

Trong POM gốc, sự khác biệt chính giữa <dependencies><dependencyManagement>là:

Hiện vật được chỉ định trong <dependencies>phần sẽ LUÔN LUÔN được đưa vào như một phần phụ thuộc của (các) mô đun con.

Các tạo phẩm được chỉ định trong phần, sẽ chỉ được bao gồm trong mô đun con nếu chúng cũng được chỉ định trong phần của chính mô đun con. Tại sao nó tốt bạn hỏi? bởi vì bạn chỉ định phiên bản và / hoặc phạm vi trong cha mẹ và bạn có thể loại bỏ chúng khi chỉ định các phụ thuộc trong POM con. Điều này có thể giúp bạn sử dụng các phiên bản hợp nhất cho các phụ thuộc cho các mô đun con, mà không chỉ định phiên bản trong mỗi mô đun con.


4

Nói theo cách riêng của tôi, bạn parent-projectgiúp bạn cung cấp 2 loại phụ thuộc:

  • phụ thuộc ngầm : tất cả các phụ thuộc được xác định trong <dependencies>phần trong của bạn parent-projectđược kế thừa bởi tất cảchild-projects
  • phụ thuộc rõ ràng : cho phép bạn chọn, các phụ thuộc để áp dụng trong của bạn child-projects. Vì vậy, bạn sử dụng <dependencyManagement>phần này, để khai báo tất cả các phụ thuộc bạn sẽ sử dụng trong khác nhau của bạn child-projects. Điều quan trọng nhất là, trong phần này, bạn xác định một <version>để bạn không phải khai báo lại trong child-project.

Theo <dependencyManagement>quan điểm của tôi (sửa tôi nếu tôi sai) chỉ hữu ích bằng cách giúp bạn tập trung hóa phiên bản phụ thuộc của bạn. Nó giống như một loại tính năng của người trợ giúp.


1

Trong Eclipse, có một tính năng nữa trong dependencyManagement. Khi dependenciesđược sử dụng mà không có nó, các phụ thuộc vô căn cứ được chú ý trong tệp pom. Nếu dependencyManagementđược sử dụng, các phụ thuộc chưa được giải quyết vẫn không được chú ý trong tệp pom và các lỗi chỉ xuất hiện trong các tệp java. (nhập khẩu và như vậy ...)


1

Sự khác biệt giữa hai yếu tố này mang lại một định nghĩa cần thiết và đầy đủ về yếu tố phụ thuộc Quản lý có sẵn trong tài liệu trang web của Maven:

quản lý phụ thuộc

"Thông tin phụ thuộc mặc định cho các dự án kế thừa từ dự án này. Các phụ thuộc trong phần này không được giải quyết ngay lập tức. Thay vào đó, khi một POM xuất phát từ dự án này tuyên bố một phụ thuộc được mô tả bởi một nhóm phù hợp và artifactId, phiên bản và các giá trị khác từ phần này được sử dụng cho sự phụ thuộc đó nếu chúng chưa được chỉ định. " [ https://maven.apache.org/ref/3.6.1/maven-model/maven.html ]

Nó nên được đọc cùng với một số thông tin có sẵn trên một trang khác:

Phần mềm .. bộ thông tin tối thiểu để khớp với tham chiếu phụ thuộc so với phần Quản lý phụ thuộc thực sự là {groupId, artifactId, type, classifier}. Trong nhiều trường hợp, các phụ thuộc này sẽ đề cập đến các tạo phẩm jar không có phân loại. Điều này cho phép chúng tôi viết tắt danh tính được đặt thành {groupId, artifactId}, vì mặc định cho trường loại là jar và trình phân loại mặc định là null. [ https://maven.apache.org/guides/int sinhtion / int sinhtion-to-dependency-mechanism.html ]

Do đó, tất cả các thành phần phụ (phạm vi, loại trừ, v.v.) của một yếu tố phụ thuộc - ngoại trừ groupId, artifactId, loại, phân loại, không chỉ phiên bản - có sẵn để khóa / mặc định tại điểm (và do đó được kế thừa từ ở đó trở đi) bạn chỉ định sự phụ thuộc trong một phụ thuộc. Nếu bạn đã chỉ định một phụ thuộc với các thành phần phụ loại và phân loại (xem trang web được trích dẫn đầu tiên để kiểm tra tất cả các thành phần phụ) tương ứng không phải là jar và không null, bạn sẽ cần {groupId, artifactId, phân loại, loại} để tham chiếu (giải quyết) sự phụ thuộc đó tại bất kỳ điểm nào trong một thừa kế có nguồn gốc từ phần tử quản lý phụ thuộc. Khác, {groupId, artifactId} sẽ đủ nếu bạn không có ý định ghi đè mặc định cho trình phân loại và loại (tương ứng jar và null). Vì vậy, mặc định là một từ khóa tốt trong định nghĩa đó; bất kỳ yếu tố phụ nào (ngoài nhómId,

Vì vậy, bất kỳ yếu tố phụ thuộc nào bên ngoài quản lý phụ thuộc, cho dù là tham chiếu đến một số yếu tố phụ thuộc quản lý hoặc dưới dạng độc lập đều được giải quyết ngay lập tức (tức là được cài đặt vào kho lưu trữ cục bộ và có sẵn cho đường dẫn lớp).

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.