Maven Archetype is a feature of Maven, you can understand it roughly, it allows us to quickly create a Maven project with a predefined structure. We just need to change values like Group Id, Artifact Id, and Version according to the value we want. I usually use Maven Archetypes to define the structure of Spring or Jakarta EE projects. In this tutorial, I will show you how to implement a Maven archetype so that if you intend to implement a Maven Archetype for yourself, you can rely on my instructions to do it!
The first thing, I need to tell you is that Maven Archetype is also a Maven project but its packaging is not jar, war, or pom but has the value “maven-archetype”. You can use the command line or a Java IDE that supports the Maven project to generate a Maven Archetype project.
Create a new Maven Archetype project
In this tutorial, I will use Eclipse IDE to work with the Maven Archetype project.
I will create a new Maven project using a Maven Archetype of Maven to generate a Maven Archetype project 🙂
This Maven Archetype is named maven-archetype-archetype:
Declare project information:
Result:
The files and folders located in the src/main/resources/archetype-resources folder will be in the project that we generate from the Maven Archetype project. For example, if you run “mvn clean install” for this project now (you should use the command line to run it, running in Eclipse may have an issue related to “${maven.home} is not specified” as a directory”) of the Maven plugin, then go to the target/test-classes/projects/id-basic/project directory, you will see a new project generated from our Maven Archetype project:
Import this Maven project using another IDE like IntelliJ, you will see the following results:
Now, we will try to implement the Maven Archetype project to our liking!
Implement Maven Archetype project
As an example for this tutorial, I will implement a Spring MVC project based on the project template of Spring Tool Suite 3, in the tutorial about Create Spring web application using Spring Legacy Project in Spring Tool Suite 3. For your understanding, Spring MVC project template of Spring Tool Suite 3 is using old versions of the Spring framework and other libraries, every time I create a new Spring MVC project using it, I have to edit it to suit the current time.
OK, let’s get started guys! You can re-read the tutorial Create Spring web application using Spring Legacy Project in Spring Tool Suite 3 to understand the directory files that you need to create in the Maven Archetype project example in this tutorial. The example project of the above tutorial, I also pushed to GitHub at https://github.com/huongdanjavacom/huongdanjava.com/tree/master/spring-mvc-example.
First, we update the pom.xml file in the src/main/resources/archetype-resources directory of the Maven Archetype project.
I will replace it with the contents of the pom.xml file in the spring-mvc-example project above, then edit the versions of the libraries and frameworks to use the latest version.
The content of the pom.xml file of the Maven Archetype project is now as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
<?xml version="1.0" encoding="UTF-8"?> <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 https://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.huongdanjava</groupId> <artifactId>springmvc</artifactId> <name>spring-mvc-example</name> <packaging>war</packaging> <version>1.0.0-BUILD-SNAPSHOT</version> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <org.springframework-version>5.3.9</org.springframework-version> <org.aspectj-version>1.9.7</org.aspectj-version> <org.slf4j-version>1.7.32</org.slf4j-version> </properties> <dependencies> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework-version}</version> <exclusions> <!-- Exclude Commons Logging in favor of SLF4j --> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework-version}</version> </dependency> <!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${org.aspectj-version}</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${org.slf4j-version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>${org.slf4j-version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${org.slf4j-version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> <exclusions> <exclusion> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> </exclusion> <exclusion> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> </exclusion> <exclusion> <groupId>com.sun.jdmk</groupId> <artifactId>jmxtools</artifactId> </exclusion> <exclusion> <groupId>com.sun.jmx</groupId> <artifactId>jmxri</artifactId> </exclusion> </exclusions> <scope>runtime</scope> </dependency> <!-- @Inject --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> <!-- Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- Test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-eclipse-plugin</artifactId> <version>2.10</version> <configuration> <additionalProjectnatures> <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature> </additionalProjectnatures> <additionalBuildcommands> <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand> </additionalBuildcommands> <downloadSources>true</downloadSources> <downloadJavadocs>true</downloadJavadocs> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <compilerArgument>-Xlint:all</compilerArgument> <showWarnings>true</showWarnings> <showDeprecation>true</showDeprecation> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.0.0</version> <configuration> <mainClass>org.test.int1.Main</mainClass> </configuration> </plugin> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.43.v20210629</version> <configuration> <webApp> <contextPath>/springmvc</contextPath> </webApp> <httpConnector> <port>8585</port> </httpConnector> </configuration> </plugin> </plugins> </build> </project> |
The problem we need to fix so that users can generate groupId, artifactId, version according to their needs is to change the configuration groupId, artifactId, version in this pom.xml file.
We will define 3 properties as ${groupId}, ${artifactId}, ${version} and then replace groupId, artifactId, version in pom.xml file with these properties, as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<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 https://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>${groupId}</groupId> <artifactId>${artifactId}</artifactId> <name>${artifactId}</name> <packaging>war</packaging> <version>${version}</version> ... </project> |
The value of these properties will be entered by the user when generating their project from our Maven Archetype project.
Next, we will copy the HomeController class, package com.huongdanjava.springmvc in the spring-mvc-example project, and put it in the src/main/resources/archetype-resources/src/main/java directory of the Maven Archetype project.
When generating a project using Maven Archetype, users can set the package name according to the name they want. The question is how our Maven Archetype project can generate the right package that the user wants. The solution here is that we will declare using properties ${package} to do this.
For example, with the HomeController class, after copying, I will edit the code that declares the package as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package ${package} import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Handles requests for the application home page. */ @Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = RequestMethod.GET) public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate); return "home"; } } |
If your Maven Archetype project has other sub-packages, for example, in your spring-mvc-example project, there is an additional package called com.huongdanjava.mvc.test, then the class, in the package com.huongdanjava.mvc .test, needs to declare the package code line as follows:
package ${package}.test;
In my example Maven Archetype project, the App class is no longer needed, so I will remove it.
For the log4j.xml file in the src/main/resources directory, I will create a new resources directory in the src/main/resources/archetype-resources/src/main directory of the Maven Archetype project, to copy this file over.
To copy the folder files in the webapp folder of the spring-mvc-example project, I will also create a new webapp folder in the src/main/resources/archetype-resources/src/main folder of the Maven Archetype project.
In the src/main/resources/archetype-resources/src/main/webapp/spring/appServlet directory, there is a servlet-context.xml file, which declares a configuration of the Spring framework component-scan, the base-package will be associated to the project that the user will generate, so I will modify it using the ${package} property as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure --> <!-- Enables the Spring MVC @Controller programming model --> <annotation-driven /> <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --> <resources mapping="/resources/**" location="/resources/" /> <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --> <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <beans:property name="prefix" value="/WEB-INF/views/" /> <beans:property name="suffix" value=".jsp" /> </beans:bean> <context:component-scan base-package="${package}" /> </beans:beans> |
The AppTest class in the src/main/resources/archetype-resources/src/test/java directory is no longer needed, you can remove it.
After having the necessary resources for the Maven project to be generated, you need to update the archetype-metadata.xml file located in the src/main/resources/META-INF/maven directory.
This file is used to define the files and folders that will be generated into the project that the user wants. By default, the contents of this file are as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?xml version="1.0" encoding="UTF-8"?> <archetype-descriptor xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" name="${artifactId}"> <fileSets> <fileSet filtered="true" packaged="true"> <directory>src/main/java</directory> </fileSet> <fileSet filtered="true" packaged="true"> <directory>src/test/java</directory> </fileSet> </fileSets> </archetype-descriptor> |
Already have the src/main/java and src/test/java directories predefined, we need to configure the src/main/webapp directory further:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="UTF-8"?> <archetype-descriptor xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" name="${artifactId}"> <fileSets> <fileSet filtered="true" packaged="true"> <directory>src/main/java</directory> </fileSet> <fileSet filtered="true"> <directory>src/main/webapp</directory> </fileSet> <fileSet filtered="true" packaged="true"> <directory>src/test/java</directory> </fileSet> </fileSets> </archetype-descriptor> |
You notice that when declaring for the webapp directory, I did not declare the “package=true” attribute in the <fileSet> tag, so that Maven Archetype does not put this folder in a package.
At this point, we have completed the implementation of a Maven Archetype project, guys!
You can run “mvn clean install” again and refresh the imported project in the IDE like me above, you will see the same result as me:
Run “mvn clean jetty:run” for this project, you will see the same results as I did in the tutorial Create Spring web application using Spring Legacy Project in Spring Tool Suite 3
One more thing, you might be wondering, where do I get the groupId, artifactId, and version of the generated project in my example? It’s in the archetype.properties file in the src/test/resources/projects/it-basic directory guys! The contents of this file are as follows:
1 2 3 4 |
groupId=archetype.it artifactId=basic-project version=0.1-SNAPSHOT package=it.pkg |
This file is used by maven-archetype-plugin to generate the example project for us.