可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
We're using maven 2.1.0. I have multiple modules that are completely separate, but still have many common dependencies. Like log4J, but some modules don't need it. I am wondering if it is a good idea to declare all common dependencies in one parent file in the <dependencyManagement>
section or is there a better way to deal with this?
A follow up question about <dependencyManagement>
. If I declare Log4J in the <dependencyManagement>
section of the parent and a sub project does not use it, will it be included anyway?
回答1:
Each module should have its own POM and where it declares its own dependencies. This not only tracks external dependencies, but also internal ones.
When you use Maven to build a project it will sort the whole lot out. So if many modules (perhaps all) depend on log4j, then it will only be included once. There are some problems if your modules depend on different versions of log4j but this approach usually works fine.
It is also useful (if there are more than 1-2 developers working together) to set up an internal repository (like Artifactory) and use that internally. It makes it much easier to deal with libraries that are not in the public repos (just add it to your internal repo!) and you can also use build tools to push builds of your own code there so other can use the modules without checking out the code (useful in larger projects)
回答2:
If you have a parent project, you can declare all dependencies and their versions in the dependencyManagement section of the parent pom. This doesn't mean that all projects will use all those dependencies, it means that if a project does declare the dependency, it will inherit the configuration, so it only need declare the groupId and artifactId of the dependency. You can even declare your child projects in the parent's dependencyManagement without introducing a cycle.
Note you can also do similar with plugins by declaring them in the pluginManagement section. This means any child declaring the plugin will inherit the configuration.
For example, if you have 4 projects, parent, core, ui and utils, you could declare all the external dependences and the internal project versions in the parent. The child projects then inherit that configuration for any dependencies they declare. If all modules are to have the same version, these can be even be declared as properties in the parent.
An example parent is as follows:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>name.seller.rich</groupId>
<artifactId>parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>name.seller.rich</groupId>
<artifactId>ui</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>name.seller.rich</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>name.seller.rich</groupId>
<artifactId>utils</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>utils</module>
<module>core</module>
<module>ui</module>
</modules>
</project>
And the utils, core, and ui projects inherit all the relevant versions.
utils:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>name.seller.rich</groupId>
<artifactId>utils</artifactId>
<!--note version not declared as it is inherited-->
<parent>
<artifactId>parent</artifactId>
<groupId>name.seller.rich</groupId>
<version>1.0.0</version>
</parent>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
</project>
core:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>name.seller.rich</groupId>
<artifactId>core</artifactId>
<parent>
<artifactId>parent</artifactId>
<groupId>name.seller.rich</groupId>
<version>1.0.0</version>
</parent>
<dependencies>
<dependency>
<groupId>name.seller.rich</groupId>
<artifactId>utils</artifactId>
</dependency>
</dependencies>
ui:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>name.seller.rich</groupId>
<artifactId>ui</artifactId>
<parent>
<artifactId>parent</artifactId>
<groupId>name.seller.rich</groupId>
<version>1.0.0</version>
</parent>
<dependencies>
<dependency>
<groupId>name.seller.rich</groupId>
<artifactId>core</artifactId>
</dependency>
</dependencies>
</project>
回答3:
I wrote up a list of best practices. Here are the most important ones.
- Always use the maven-enforcer-plugin
- Enforce dependency convergence
- Otherwise it's possible that you depend on two different jars which both depend on log4j. Which one gets used at compile time depends on a set of rules that you shouldn't have to remember. They can both (!) get exported as transitive dependencies.
- Require plugin versions (for all plugins, even the built in ones)
- Define them in pluginManagement in the parent pom to define versions
- Otherwise a new version of maven-surefire-plugin could break your build
- Use dependencyManagement in the parent pom to use versions consistently across all modules
- Periodically run mvn dependency:analyze
- It's possible that you're getting a dependency transitively that you directly depend on at compile time. If so, it's important to add it to your pom with the version you require. This plays nicely with the enforcer plugin.
- It's possible that you're declaring extra dependencies that you don't use. This doesn't work properly 100% of the time, especially with libraries that are designed to have optional pieces (i.e. slf4j-api gets detected properly, but slf4j-log4j12 fails).
回答4:
A follow up question about . If I declare Log4J in the section of the parent and a sub project does not use it, will it be included anyway?
No. Dependency management only sets the default version and possibly scope (I've seen this both appear to be inherited and appear to not be inherited so you will need to look this one up on your own). To include the dependency in a child module, you need to declare it as a dependency of the module and omit the version element. You can override the default in a child module simply be including the version number in the dependency element of the child module's POM.
I have multiple modules that are completely separate, but still have many common dependancies.
In this case, yes and no.
For modules that are built, versioned, and deployed together as a unified project, for instance the modules that compose a single Web application, most definitely yes. You want to relieve yourself of the headache of changing the version in more than one POM when you decide to move to a new version of a dependency. It can also save you work when you need to exclude certain transitive dependencies. If you declare the dependency with its excludes in the section you don't have to maintain the exclusions in multiple POMs.
For modules that are not directly related but are built within a single team within the company you may want to consider declaring default versions for common libraries like testing utilities, logging utilities, etc. in order to keep the team working with the standard versions of the tools that you have defined as part of your best practices. Remember you can always increase the version of your super POM when you standardize on a new set of common libraries. Where you draw the line between standardized library and tools and project specific libraries and tools is up to you but it should be easy for your team to find.
回答5:
We use a single common parent with a dependencyManagement block for all our projects. This is starting to break down as we move more projects into maven - if a project needs a different version then we have to either declare it as a dependency for all children or explicitly define the version for each pertinent child.
We're trying out a model where we split the dependencyManagement out from our common parent and then import our corporate dependencyManagement pom into the top level project pom. This allows us to selectively define project defaults that override the corporate defaults.
Here is the original scenario:
A defines version 1.0 of foo.jar as the corporate default
B child of A
C1, C2, C3 children of B
D1, D2, D3 children of C1, C2, C3 respectively
If D1 and D2 require version 1.1 of foo.jar, then our choice used to be:
- Declare foo.jar version 1.1 as a dependency in B, making it appear that C1, C2, C3 and D3 also depended upon version 1.1
- Declare foo.jar version 1.1 as a dependency in D1 and D2, moving the dependency declaration into multiple places deeper in our project hierarchy.
Here is what we're trying out:
A defines version 1.0 of foo.jar as the corporate default
B dependencyManagement: imports A, declares a default of foo.jar version 1.1
C1, C2, C3 children of B
D1, D2, D3 children of C1, C2, C3 respectively
Now D1 and D2 just declare a dependency upon foo.jar and pick up version 1.1 from the dependencyManagement block of B.
回答6:
In a multi-module project I place any common dependencies in the element of the parent pom.xml. I'm not sure if this would be best practice if the modules were not related to the same project though.