Maven is an excellent open source framework for project management, and its plug-in mechanism provides great convenience for its function expansion. In most cases, we don't need to develop Maven plug-ins ourselves, because Maven itself provides many convenient official plug-ins. However, for some special scenarios and special needs, developing a custom Maven plug-in can greatly improve the development efficiency. This article shows how to create a custom Maven plug-in with a simple example.
Maven plug-in development
This article will take a self-made Maven plug-in as an example to show how to use Idea to build a self-made Maven plug-in from 0.
Maven plug-in target
Maven is essentially a plug-in execution framework, and all work is done through plug-ins. Including common commands such as install/clean/deploy/compiler, the bottom layer is actually the execution result of Maven plug-ins one by one.
Plug in Goal: develop a self-made Maven plug-in called demo test Maven plugin, which contains two goals:
- Goal1: execute the command MVN demo test: goal1, and the console outputs This is goal1;
- Goal2: execute the command MVN demo test: goal2, and output This is goal2. To the console;
Plug in project naming
Before developing the Maven plug-in, the first thing is to determine the plug-in project name. Maven plug-ins have specific naming conventions, which can be divided into two types:
- Official plug-in project naming specification: Maven xxx plugin, where the plug-in name is xxx. In maven, you can use the command mvn xxx to call the plug-in;
- Custom plug-in project naming specification: xxx Maven plugin, where the plug-in name is xxx. In maven, you can use the command mvn xxx to call the plug-in;
Our plug-in is a custom plug-in. The plug-in name is demo test, so the project name of the plug-in is demo test Maven plugin.
Create plug-in project
After confirming that the project name is demo test Maven plug, we can actually create a project. This section uses Idea as a tool to introduce how to create a maven plug-in project.
Create a maven project in Idea and select org apache. maven. Archetypes: Maven archetype mojo as the project template;
Enter the project name in Idea and click OK;
Finally, click Finish to complete the creation of the project;
Through the above steps, we created an empty Maven plug-in project, which currently has no code.
Plug in project analysis
Through the above steps, we have created an empty Maven project. The project directory is as follows:
! [create project 4]
The contents of the automatically generated project POM file are as follows, of which the following are key:
- Packaging: the packaging of ordinary Java projects is jar/war/pom, but the packaging of Maven plug-in projects is Maven plug-in;
- Maven plugin API: Maven plug-in project relies on Maven plugin API package, which is the core package that Maven plug-in project must rely on;
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.demo.test</groupId> <artifactId>demo-test-maven-plug</artifactId> <packaging>maven-plugin</packaging> <version>1.0-SNAPSHOT</version> <name>demo-test-maven-plug Maven Mojo</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
The automatically generated project also contains a Java file, as shown below. This Java file has several characteristics:
- The generated class inherits AbstractMojo. Here is the specification requirement of Maven plug-in. Maven plug-in must inherit AbstractMojo and abstract execute method;
- There are some tags on classes and methods, such as @ goal. Unlike annotations, tags appear in related documents;
- Tag @goal is used to specify the name of the plug-in. For example, in the command mvn clean, clean is @ goal;
- Tag @phase is used to bind the generation cycle executed by the plug-in, such as the clean cycle. When mvn clean is executed, the plug-in will be automatically triggered;
- Tag @parameter is used to specify the parameters of the plug-in.
There are many problems with Tag mechanism, such as no syntax error. Why does Maven use Tag instead of annotation? This is because Java did not support annotations in the early days, jdk1 5 before adding annotation function. At the beginning of Maven development, there was no annotation, so Tag can only be used instead. Annotation can be used instead of Tag in the new version of Maven.
package org.demo.test; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import java.io.File; import java.io.FileWriter; import java.io.IOException; /** * Goal which touches a timestamp file. * * @goal touch * * @phase process-sources */ public class MyMojo extends AbstractMojo { /** * Location of the file. * @parameter expression="${project.build.directory}" * @required */ private File outputDirectory; public void execute() throws MojoExecutionException { File f = outputDirectory; if ( !f.exists() ) { f.mkdirs(); } File touch = new File( f, "touch.txt" ); FileWriter w = null; try { w = new FileWriter( touch ); w.write( "touch.txt" ); } catch ( IOException e ) { throw new MojoExecutionException( "Error creating file " + touch, e ); } finally { if ( w != null ) { try { w.close(); } catch ( IOException e ) { // ignore } } } } }
Modify project POM
The default POM version is old and lacks functions, so you need to modify the POM in the automatically generated project before development:
- Upgrade Maven plugin API package version to 3.8.3. The version of automatically generated POM is 2.0 of the lower version;
- Add Maven plugin annotations dependency. This package contains Maven related annotations, which can replace Tag;
- Add Maven plugin plugin dependency. This package enables the plugin to support jdk1 Version above 8;
The modified POM is as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.demo.test</groupId> <artifactId>demo-test-maven-plugin</artifactId> <packaging>maven-plugin</packaging> <version>1.0-SNAPSHOT</version> <name>demo-test-maven-plug Maven Mojo</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>3.8.3</version> </dependency> <dependency> <groupId>org.apache.maven.plugin-tools</groupId> <artifactId>maven-plugin-annotations</artifactId> <version>3.6.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugin-plugin</artifactId> <version>3.6.1</version> </plugin> </plugins> </build> </project>
Add custom Mojo
As mentioned at the beginning, the plug-in in this project requires two goal s:
- Goal1: execute the command MVN demo test: goal1, and the console outputs This is goal1;
- Goal2: execute the command MVN demo test: goal2, and output This is goal2. To the console;
In Maven plug-in development, goal corresponds to Mojo class, so we also need to implement two Mojo classes, as shown below, which is different from the Mojo class generated by default:
- @Mojo annotation: defines the information related to the plug-in, for example, name indicates the name of goal;
- @Parameter annotation: Specifies the parameters used by the plug-in when running. Name is the parameter name and defaultValue is the default value;
@Mojo(name = "goal1") public class Goal1Mojo extends AbstractMojo { @Parameter(name = "name", defaultValue = "test") private String name; public void execute() { getLog().info("This is goal1."); } } @Mojo(name = "goal2") public class Goal2Mojo extends AbstractMojo { @Parameter(name = "name", defaultValue = "test") private String name; public void execute() { getLog().info("This is goal2."); } }
Use of plug-ins
Through the above steps, we have simply implemented a Maven plug-in. The name of the plug-in is demo test. The plug-in contains two goals: goal1 and goal2. Next, we compile and publish the plug-in project. The compilation and publishing steps are not described in detail here.
After the project is compiled and released, we introduce the plug-in into the POM of another project:
<build> <plugins> <plugin> <groupId>org.demo.test</groupId> <artifactId>demo-test-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> </plugin> </plugins> </build>
- Execute MVN demo test: goal1 on the command line of the project, and you can see the following output:
[INFO] Scanning for projects... [INFO] [INFO] ---------------------< com.example:javafx-spring >---------------------- [INFO] Building javafx-spring 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- demo-test-maven-plugin:1.0-SNAPSHOT:goal1 (default-cli) @ javafx-spring --- [INFO] This is goal1. [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.470 s [INFO] Finished at: 2021-11-18T17:55:11+08:00 [INFO] ------------------------------------------------------------------------
- Execute MVN demo test: goal2 on the command line of the project, and you can see the following output:
[INFO] Scanning for projects... [INFO] [INFO] ---------------------< com.example:javafx-spring >---------------------- [INFO] Building javafx-spring 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- demo-test-maven-plugin:1.0-SNAPSHOT:goal2 (default-cli) @ javafx-spring --- [INFO] This is goal2. [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.540 s [INFO] Finished at: 2021-11-18T17:57:46+08:00 [INFO] ------------------------------------------------------------------------
So far, our Maven plug-in has been developed to achieve the purpose set at the beginning.
Appendix 1: detailed explanation of Maven notes
import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Execute; import org.apache.maven.plugins.annotations.InstantiationStrategy; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import org.apache.maven.settings.Settings; // The name of the target corresponding to this Mojo @Mojo( name = "<goal-name>", aggregator = <false|true>, configurator = "<role hint>", // Execution strategy executionStrategy = "<once-per-session|always>", inheritByDefault = <true|false>, // Instantiation strategy instantiationStrategy = InstantiationStrategy.<strategy>, // If the user does not explicitly set the phase to which this Mojo is bound in the POM, bind a MojoExecution to that phase defaultPhase = LifecyclePhase.<phase>, requiresDependencyResolution = ResolutionScope.<scope>, requiresDependencyCollection = ResolutionScope.<scope>, // Prompt that this Mojo needs to be called directly (not bound to the lifecycle phase) requiresDirectInvocation = <false|true>, // Prompt: This Mojo cannot run in offline mode requiresOnline = <false|true>, // Tip: This Mojo must be run within a Maven project requiresProject = <true|false>, // Prompt whether this Mojo is thread safe. Thread safe Mojo supports concurrent calls in parallel construction threadSafe = <false|true> ) // (since Maven 3.0) // When to execute this Mojo @Execute( goal = "<goal-name>", // If goal is provided, this Mojo is executed in isolation phase = LifecyclePhase.<phase>, // This Mojo is automatically executed during this lifecycle phase lifecycle = "<lifecycle-id>" ) // Execute this Mojo in this lifecycle public class MyMojo extends AbstractMojo { @Parameter( name = "parameter", // Aliases can be used to configure parameters in POM alias = "myAlias", property = "a.property", defaultValue = "an expression, possibly with ${variables}", readonly = <false|true>, required = <false|true> ) private String parameter; @Component( role = MyComponentExtension.class, hint = "..." ) private MyComponent component; @Parameter( defaultValue = "${session}", readonly = true ) private MavenSession session; @Parameter( defaultValue = "${project}", readonly = true ) private MavenProject project; @Parameter( defaultValue = "${mojoExecution}", readonly = true ) private MojoExecution mojo; @Parameter( defaultValue = "${plugin}", readonly = true ) private PluginDescriptor plugin; @Parameter( defaultValue = "${settings}", readonly = true ) private Settings settings; @Parameter( defaultValue = "${project.basedir}", readonly = true ) private File basedir; @Parameter( defaultValue = "${project.build.directory}", readonly = true ) private File target; public void execute() { } }
Reference articles
[maven] maven plug-in development practice
This article is first released to WeChat official account, all rights reserved, no reprint!