In the previous article in this column, we discussed some simple and practical pom refactoring techniques, including the premise of refactoring-continuous integration, and how to improve the readability and build stability of POM by adding or deleting content. However, in actual projects, these skills are not enough. It is particularly worth mentioning that the actual Maven projects are mostly multi-module, if we only refactor a single pom without considering the relationship between modules, it will lead to unnecessary duplication. This article discusses several multi-module-based pom reconstruction techniques.
Repeated or repeated
Programmers should have a general sense of smell, and should be able to smell the most common bad smell of repetition. No matter what the coat of repetition is, once discovered, they should be completely wiped out without mercy. Do not indulge in repeated fermentation here because Pom is not a product code. For example, a piece of code is repeated:
<dependency> <groupId>org.springframework</groupId> <artifactid>spring-beans</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactid>spring-context</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactid>spring-core</artifactId> <version>2.5</version> </dependency>
Will you use springframework components of different versions in a project? The answer is obviously no. Therefore, there is no need to repeat <version> 2.5 </version> three times. The Maven attribute is used to extract 2.5 as follows:
<properties> <spring.version>2.5</spring.version> </properties> <depencencies> <dependency> <groupId>org.springframework</groupId> <artifactid>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactid>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactid>spring-core</artifactId> <version>${spring.version}</version> </dependency> </depencencies>
At present, 2.5 only appears in one place. Although the Code is a little longer, it disappears again. When you upgrade the dependent version in the future, you only need to modify one place, and you can avoid missing a dependency upgrade.
Readers may already be very familiar with this example. I will try again here to pave the way for the future. The purpose of multi-module pom reconstruction is the same as that of this example, but also to eliminate duplicates, the more modules, the more potential duplicates, and the more necessary the refactoring.
Eliminate repeated multi-module dependency configurations
Consider such a small project. It has more than 10 Maven modules with clear division of labor and different duties. The coupling between these modules is relatively small, in this way, you can focus on developing your own modules without having to think too much about the impact of others on yourself. (Well, I admit this is an ideal situation.) When I start coding module A, I first need to introduce some common dependencies such as JUnit and log4j:
<dependency> <groupId>junit</groupId> <artifactid>junit</artifactId> <version>4.8.2</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactid>log4j</artifactId> <version>1.2.16</version> </dependency>
My colleague is developing Module B. He also uses JUnit and log4j (we have discussed in our meeting that the unified unit test framework is JUnit rather than testng, and the unified log is implemented as log4j rather than Jul, I will not explain why I made this decision. In short, it will be settled ). My colleague wrote the following dependency Configuration:
<dependency> <groupId>junit</groupId> <artifactid>junit</artifactId> <version>3.8.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactid>log4j</artifactId> <version>1.2.9</version> </dependency>
Are there any problems? Yes, he missed the scope on which JUnit depends, because he is not familiar with Maven. What's the problem? Yes, version! Although he and I both depend on JUnit and log4j, the version is different. We held a meeting to discuss the specific version, but it would be very dangerous if a project depends on multiple versions of a class library at the same time! OK. Now there are only two dependencies between the two modules. There is no problem with manual repair. But if there are 10 modules, what about 10 dependencies or more for each module? It seems that this is a quagmire, and it is hard to clean up once it gets stuck.
Fortunately, Maven provides an elegant solution to solve this problem by using the Inheritance Mechanism and the dependencymanagement element. Note that dependencymananget is used instead of dependencies. You may have thought of configuring dependencies in the parent module, so that all sub-modules are automatically inherited, which not only achieves the purpose of dependency consistency, but also saves a lot of code, however, this is problematic. For example, if you extract the dependency of Module C from spring-AOP to the parent module, however, although modules A and B do not need spring-AOP, but it also inherits directly. Dependencymanagement,Dependencymanagement only affects the configurations of existing dependencies, but does not introduce dependencies.. For example, we can configure the following in the parent module:
<dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactid>junit</artifactId> <version>4.8.2</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactid>log4j</artifactId> <version>1.2.16</version> </dependency> </dependencies> </dependencyManagement>
This configuration does not introduce dependencies to any sub-modules. However, if a sub-module needs to use JUnit and log4j, we can simplify the dependency configuration as follows:
<dependency> <groupId>junit</groupId> <artifactid>junit</artifactId> </dependency> <dependency> <groupId>log4j</groupId> <artifactid>log4j</artifactId> </dependency>
Now you only need groupid and artifactid. Other elements such as version and scope can be obtained by inheriting the dependencymanagement of the parent Pom. If the dependency is configured with exclusions, the code saved will be more impressive. However, the focus is not on this, but on ensuring that the dependency configurations of JUnit and log4j used by all modules are consistent. In addition, sub-modules can still introduce dependencies as needed. If I do not set dependency, the spring-AOP dependency under dependencymanagement in the parent module will not affect me.
Maybe you have realized that,In multi-module Maven projects, dependencymanagement is almost indispensable, because only it can effectively help us maintain dependency consistency..
I would like to introduce dependencymanagement, but a discussion with sunng a few days ago gave me more content to share. That is, when dependencymanagement is used, we can use a special import scope dependency instead of inheriting from the parent module. Sunng lists it as its Maven recipe #0. Let me briefly introduce it.
We know that Maven's inheritance is the same as Java's inheritance, and multiple inheritance cannot be implemented. If 10, 20, or more modules inherit from the same module, according to our previous practice, the dependencymanagement of this parent module will contain a large number of dependencies. If you want to classify these dependencies for clearer management, it is impossible. The import scope dependency can solve this problem. You can put dependencymanagement in a separate pom dedicated for managing dependencies, and then introduce dependencymanagement through the import scope dependency in the module where dependencies need to be used. For example, you can write such a pom for dependency management:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.sample</groupId> <artifactId>sample-dependency-infrastructure</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactid>junit</artifactId> <version>4.8.2</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactid>log4j</artifactId> <version>1.2.16</version> </dependency> </dependencies> </dependencyManagement> </project>
Then, I can introduce the dependency management configuration in non-inheritance mode:
<dependencyManagement> <dependencies> <dependency> <groupId>com.juvenxu.sample</groupId> <artifactid>sample-dependency-infrastructure</artifactId> <version>1.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependency> <groupId>junit</groupId> <artifactid>junit</artifactId> </dependency> <dependency> <groupId>log4j</groupId> <artifactid>log4j</artifactId> </dependency>
In this way, the POM of the parent module will be very clean, and the dependency is managed by a dedicated packaging pom, which also fits with the single Responsibility Principle in the object-oriented design. In addition, we can create multiple such dependency management POM to manage dependencies in a more detailed manner. This method is similar to the combination in object-oriented design rather than inheritance.
Eliminate repeated multi-module plug-in configurations
Similar to dependencymanagement, you can also use the pluginmanagement Element Management plug-in. A common usage is that we want all modules of the project to use Maven compiler plugin, Java 1.5, and specify the Java source file encoding as UTF-8, you can configure pluginmanagement In the POM of the parent module as follows:
<build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.5</source> <target>1.5</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </pluginManagement> </build>
This configuration will be applied to Maven-compiler-plugin all sub-modules. Because Maven has built-in Maven-compiler-plugin and is bound to the lifecycle, therefore, the sub-module no longer requires any Maven-compiler-plugin configuration.
Different from dependency configuration, usually all projects have uniform configurations for any dependency, but the plug-in is not like this. For example, you can want module A to run all unit tests, module B needs to skip some tests. In this case, you need to configure Maven-surefire-plugin, so that the plug-in configurations of the two modules are inconsistent. That is to say, simply extracting plug-in configurations to the pluginmanagement of the parent Pom is often not suitable for all situations, so we need to pay attention when using it, only General Plug-in configurations should be extracted to the parent pom using pluginmanagement.
With regard to plug-in pluginmanagement, Maven does not provide management in a similar way as the import scope dependency, so we can only use the inheritance relationship. Fortunately, the number of plug-in configurations is far less than that of dependency configurations, therefore, this is not a problem.
Summary
The introduction to Maven pom refactoring has come to an end here. Basically, if you have mastered the refactoring skills described in this article and the previous Maven column and understood the objective principles behind it, you will surely make the project pom clearer and easier to understand, it can also avoid some potential risks as soon as possible. Although Maven is only a tool to help you build projects and manage dependencies, Pom is not part of your official product code. However, we should also take pom seriously. This is a bit like the test code. In the past, everyone may think that the test code is dispensable and will not re-engineer and optimize the test code, however, as agile development and TDD are becoming more and more accepted, more and more developers are paying attention to testing code. Therefore, I hope that you will not only be satisfied with a "usable" pom, but will be able to actively fix the bad taste in pom.