maven-archetype-plugin 原型插件详解

原型插件的作用与使用流程

可以理解为是一个助力于生成一个脚手架项目的插件,内部依赖 Velocity 模板引擎。

应用流程:

  1. 根据编写的原型元工程,通过 maven archetype 插件,帮助我们生成一个脚手架工程(原型工程)。
  2. 对脚手架工程(原型工程)进行打包,生成最终的可运行的脚手架工程。
  3. 执行可运行的脚手架工程(期间可输入脚手架定义的参数)即可创建出我们需要的目标项目。

各个阶段的工程结构

1. 原型元工程

原型元工程
pom.xml 中配置原型插件:

<build>
	<plugins>
	    <!-- 非必要 -->
		<plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-release-plugin</artifactId>
             <version>2.5.1</version>
             <dependencies>
                 <dependency>
                     <groupId>org.apache.maven.shared</groupId>
                     <artifactId>maven-invoker</artifactId>
                     <version>2.2</version>
                 </dependency>
             </dependencies>
         </plugin>
 		
 		 <!-- 必要:原型插件 -->
         <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-archetype-plugin</artifactId>
             <version>2.0-alpha-4</version>
         </plugin>
	</plugins>
</build>

2. 原型工程

在原型元工程的 POM 目录下(maven-archetype-test)执行:

mvn archetype:create-from-project

即可在maven-archetype-test/target/generated-sources/archetype 目录下生成原型工程。


注意:原型元工程中的 prv.cqq.mavenarchetype 包层级已经被剔除,切换到了根路径。后面根据可运行的原型工程生成目标项目时,可动态切换为用户输入的设置路径。

原型工程
生成的与 src 目录同级最外层的原型工程的 pom.xml 的内容如下:

注意:原型工程的坐标信息 artifactId 会默认追加 -archetype 后缀

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>priv.cqq</groupId>
    <!-- 坐标信息中追加了 -archetype 后缀 -->
    <artifactId>maven-archetype-test-archetype</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>maven-archetype</packaging>
    <name>maven-archetype-test-archetype</name>
    <build>
    	<!-- 自动生成的原型打包插件 -->
        <extensions>
            <extension>
                <groupId>org.apache.maven.archetype</groupId>
                <artifactId>archetype-packaging</artifactId>
                <version>2.0-alpha-4</version>
            </extension>
        </extensions>
    	<!-- 打包插件 -->
        <plugins>
            <plugin>
                <artifactId>maven-archetype-plugin</artifactId>
                <version>2.0-alpha-4</version>
                <extensions>true</extensions>
            </plugin>
        </plugins>
    </build>
</project>

3. 可运行的原型工程

在原型工程的 POM 目录下(maven-archetype-test/target/generated-sources/archetype )执行:

mvn install

即可在 target 目录下生成可运行的原型工程:

可运行的原型工程

4. 执行可运行的原型工程生成目标项目

mvn archetype:generate 
-DinteractiveMode=false
-DarchetypeCatalog=local
-DarchetypeGroupId=priv.cqq
-DarchetypeArtifactId=maven-archetype-test-archetype
-DarchetypeVersion=0.0.1-SNAPSHOT
-DgroupId=org.cqq
-DartifactId=targetapp
-Dpackage=org.cqq.targetapp
-Dversion=0.0.1-SNAPSHOT
-DarchetypeCatalog=local

mvn archetype:generate -DinteractiveMode=false -DarchetypeCatalog=local -DarchetypeGroupId=priv.cqq -DarchetypeArtifactId=maven-archetype-test-archetype -DarchetypeVersion=0.0.1-SNAPSHOT -DgroupId=org.cqq -DartifactId=targetapp -Dpackage=org.cqq.targetapp -Dversion=0.0.1-SNAPSHOT

命令的参数可分为三部分:

  1. 功能参数:
    -DinteractiveMode=false:进入交互模式
    -DarchetypeCatalog=local:用来指定 maven-archetype-plugin 读取 archetype-catalog.xml 文件的位置(archetype-catalog.xml 的作用会在后面说到):

    • internal:maven-archetype-plugin 内置
    • local:本地,位置为~/.m2/archetype-catalog.xml
    • remote:Maven中央仓库
  2. 自定义可运行原型工程的坐标信息:
    -DarchetypeGroupId: 自定义 archetype 的 groupId
    -DarchetypeArtifactId: 自定义 archetype 的 artifactId
    -DarchetypeVersion: 自定义 archetype 的版本号

  3. 需要生成的目标项目的坐标信息:
    -DgroupId: 要生成的目标项目的 groupId
    -DartifactId: 要生成的目标项目的artifactId
    -Dpackage: 要生成的目标项目的包路径(正是用来填补上面提到的原型工程中被剔除的包路径,如果没有设置则默认值为 DgroupId + DartifactId)
    -Dversion: 要生成的目标项目的版本号

最终生成的目标项目结构如下:

D:\development\idea\workspace\mvn-archetype>cd targetapp

D:\development\idea\workspace\mvn-archetype\targetapp>tree /F
D:.
│  .gitignore
│  pom.xml
│
└─src
    ├─main
    │  ├─java
    │  │  └─org
    │  │      └─cqq
    │  │          └─targetapp
    │  │                  MavenArchetypeTestApplication.java
    │  │
    │  └─resources
    │      │  application.yaml
    │      │  logback-spring-dev.xml
    │      │
    │      └─META-INF
    │              spring.factories
    │
    └─test
        └─java
            └─org
                └─cqq
                    └─targetapp
                            MavenArchetypeTestApplicationTests.java

5. 小结

  1. 原型元工程中的代码可以理解为是最终生效模版文件的前身。生成模板文件后,可以二次进行修改,即原型元工程中的代码不是最终的模板内容,而是帮助我们生成原型工程。
  2. 原型元工程到原型工程的过渡,依赖元工程中的插件:maven-archetype-plugin
  3. 元工程中的插件 maven-archetype-plugin 使得原型工程中的最外层生成一个包含了原型打包(打包 archetype-resources 中的模板文件)依赖的 pom ,对生成的原型工程 install 即可生成最终的可执行的原型工程

archetype-catalog.xml

该文件用于说明去哪里寻找可运行原型工程来生成目标项目。前面我们使用的是 -DarchetypeCatalog=local 参数,也说明了参数值为 local 时会去寻找本地~/.m2/ 目录下寻找 archetype-catalog.xml 文件。下面看一下该文件的内容:

<?xml version="1.0" encoding="UTF-8"?><archetype-catalog>
  <archetypes>
    <archetype>
      <groupId>priv.cqq</groupId>
      <artifactId>maven-archetype-test-archetype</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <description>maven-archetype-test-archetype</description>
    </archetype>
  </archetypes>
</archetype-catalog>

里面描述的正是原型工程 mvn install 后坐标信息,也说明了是在 install 后在该文件中添加坐标信息的。

原型工程中的 META-INF 目录

该目录下包含两个文件:archetype.xmlarchetype-metadata.xml

1. archetype.xml

该文件中的内容很简单:资源文件清单。

<?xml version="1.0" encoding="UTF-8"?>
<archetype>
    <id>maven-archetype-test</id>
    <sources>
        <source>src\main\java\priv\cqq\mavenarchetypetest\MavenArchetypeTestApplication.java</source>
    </sources>
    <testSources>
        <source>src\test\java\priv\cqq\mavenarchetypetest\MavenArchetypeTestApplicationTests.java</source>
    </testSources>
    <resources>
        <resource>src\main\resources\application.yaml</resource>
        <resource>src\main\resources\logback-spring-dev.xml</resource>
        <resource>src\main\resources\META-INF\spring.factories</resource>
        <resource>.gitignore</resource>
    </resources>
</archetype>

2. archetype-metadata.xml

官方文档。该文件是元数据文件,主要使用两类标签:

  1. <requiredProperties>:自定义参数,可在模板文件中通过 ${} 的形式进行值引用
  2. fileSets:配置模板文件扫描规则。filtered 设置为 true 时,才会解析模板文件中的值引用,替换为具体值。
<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor name="maven-archetype-test">
    <requiredProperties>
        <requiredProperty key="customParam1">
            <defaultValue>customParam1</defaultValue>
        </requiredProperty>
        <requiredProperty key="customParam2">
            <defaultValue>customParam2</defaultValue>
        </requiredProperty>
    </requiredProperties>
    
    <fileSets>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" encoding="UTF-8">
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.factories</include>
                <include>**/*.yaml</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/test/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet encoding="UTF-8">
            <directory></directory>
            <includes>
                <include>.gitignore</include>
            </includes>
        </fileSet>
    </fileSets>
</archetype-descriptor>

思考一下,以上的自定义参数配置的意义是什么呢?用途在哪里?

当我们通过 idea 创建项目时,实际上,是可以选择 Archetype 的。idea 默认为我们提供了一些原型模板,当我们选中后,根据该模板会快速生成一个 maven 项目。

idea 创建项目

那么模板内容不能是死的对吧,可以根据用户的一些输入生成不同的内容。所以,当我们在编写自己的 Archetype 时,也应当如此。比如将一些配置:数据库连接配置、Redis 连接配置、Swagger 配置、MQ 配置、日志存储路径等等,都可以提取出配置项。这样,可使得最终生成的目标项目是能直接运行的,而不用找到各个配置类、文件,修改后才能运行。

值引用实践

清楚了 archetype-metadata.xml 的作用后,那么就来看下如何应用,以及有哪些需要注意的点。

  1. 执行命令生成原型项目:

    mvn clean archetype:create-from-project

  2. 在原型项目的 archetype-metadata.xml 中声明自定义属性:

    <requiredProperties>
        <requiredProperty key="application-name">
            <defaultValue>WEB-APP</defaultValue>
        </requiredProperty>
        <requiredProperty key="log-home">
            <defaultValue>./applog</defaultValue>
        </requiredProperty>
        <requiredProperty key="enable-auto-configuration-classes">
            <defaultValue>com.xxx.xxx.SpringUtils</defaultValue>
        </requiredProperty>
        <requiredProperty key="component-scan-path">
            <defaultValue>com.xxx.xxx</defaultValue>
        </requiredProperty>
    </requiredProperties>
    
  3. 检查 <fileSet> 标签中的 filtered 属性是否为 true & include 标签中的引入规则

    <?xml version="1.0" encoding="UTF-8"?>
    <fileSets>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" encoding="UTF-8">
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </fileSet>
    
    	<!-- 默认生成内容 -->
        <!-- <fileSet encoding="UTF-8"> -->
        <!--   <directory>src/main/resources</directory> -->
        <!--   <includes> -->
        <!--     <include>**/*.factories</include> -->
        <!--     <include>**/*.yaml</include> -->
        <!--   </includes> -->
        <!-- </fileSet> -->
    	<!-- 补齐 filtered 属性 -->
        <fileSet filtered="true" encoding="UTF-8">
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.factories</include>
                <include>**/*.yaml</include>
            </includes>
        </fileSet>
        
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/test/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet encoding="UTF-8">
            <directory></directory>
            <includes>
                <include>.gitignore</include>
            </includes>
        </fileSet>
    </fileSets>
    
  4. 模板文件中引用

    4.1 MavenArchetypeTestApplication (需修正)

    #set( $symbol_pound = '#' )
    #set( $symbol_dollar = '$' )
    #set( $symbol_escape = '\' )
    package ${package};
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.ComponentScan;
    
    @ComponentScan(value = "${symbol_dollar}{component-scan-path}")
    @SpringBootApplication
    public class MavenArchetypeTestApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MavenArchetypeTestApplication.class, args);
        }
    
    }
    
    // ====================== 修正值引用的取值方式 =======================
    
    @ComponentScan(value = "${component-scan-path}")
    @SpringBootApplication
    public class MavenArchetypeTestApplication {
        public static void main(String[] args) {
            SpringApplication.run(MavenArchetypeTestApplication.class, args);
        }
    }
    

    4.2 spring.factories(无需修正)

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=${enable-auto-configuration-classes}
    

    4.3 application.yaml(无需修正)

    server:
      port: 8080
      servlet:
        context-path: /
        session:
          timeout: PT1H
    
    spring:
      application:
        name: ${application-name}	
    

    4.4 logback-spring-dev.xml(需修正)

    #set( $symbol_pound = '#' )
    #set( $symbol_dollar = '$' )
    #set( $symbol_escape = '\' )
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration debug="false" scan="true" scanPeriod="1 seconds">
    
        <contextName>logback-spring</contextName>
        <springProperty scope="context" name="springProfile" source="spring.profiles.active"/>
        
        <!-- <property name="LOG_HOME" value="${symbol_dollar}{logHome}"/> -->
        <!-- 修正值引用的取值方式 -->
    	<property name="LOG_HOME" value="${logHome}"/>
        <property name="FILE_PATTERN"
                  value="%yellow([${symbol_dollar}{springProfile:-}]) %magenta([%X{TraceId}]) %magenta(%d{yyyy-MM-dd HH:mm:ss.SSS}) %yellow(%-5level) %magenta(${symbol_dollar}{PID:-}) %-20(%yellow([%20.20thread])) %cyan([%-40logger{40}:%L]) -> %msg%n"/>
    
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>${symbol_dollar}{FILE_PATTERN}</pattern>
                <charset>utf-8</charset>
            </encoder>
        </appender>
    
        <root level="info">
            <appender-ref ref="STDOUT"/>
        </root>
    </configuration>
    

    4.5 效果

    mvn install 原型工程后,生成目标项目:

    mvn archetype:generate -DinteractiveMode=false -DarchetypeGroupId=priv.cqq -DarchetypeArtifactId=maven-archetype-test-archetype -DarchetypeVersion=0.0.1-SNAPSHOT -DgroupId=org.cqq -DartifactId=targetapp -Dpackage=org.cqq.targetapp -Dversion=0.0.1-SNAPSHOT

    java值引用factories值引用
    yaml值引用
    xml值引用

值引用的转译问题

可以看到,前面分别对于 java、xml 文件中的值引用进行了修正,就是在解决 maven archetype plugin 使用的 Velocity 模板对特殊字符转的译问题。

1. 不修正

先说如果不修正的情况,以需要修正的 logback-spring-dev.xml 为例:
<property name="LOG_HOME" value="${symbol_dollar}{logHome}"/>,对于这段 xml,在最终生成的目标项目中的输出内容为 <property name="LOG_HOME" value="${logHome}"/>,即不会进行值替换,保留原有内容。

2. 修正后

删除{symbol_dollar}后,仅保留:${logHome},这样就可以正确的读取到自定义变量。

3. 原因分析

换个角度看,也不能定义为一个问题。因为 maven-archetype 并不知道模板文件中的这个值引用是在引用目标项目自身的配置文件中的属性值,还是要引用用于构建目标项目的自定义属性值。所以统一进行了转译,保留原本的内容。

4. 为什么只有 java、xml 模板文件中转译问题

为什么 spring.factories / application.yaml 没有这个问题呢?在回顾一下 archetype-metadata.xml 中我们是不是补齐了一个 fileSet 的 filtered 属性?原 fileSet 中过滤的正是 factories 与 yaml 类型文件,但是没有 filtered 属性。

<!-- <fileSet encoding="UTF-8"> -->
<!--   <directory>src/main/resources</directory> -->
<!--   <includes> -->
<!--     <include>**/*.factories</include> -->
<!--     <include>**/*.yaml</include> -->
<!--   </includes> -->
<!-- </fileSet> -->

所以,可以得出两个结论:

  1. 执行 mvn archetype:generate 默认只会对 java、xml 类型文件进行转译,并在元数据文件中的 fileSet 标签中设置 filtered 属性为 true
  2. 被转译的文件中的 #$\ 特殊符号会被转译
    #set( $symbol_pound = '#' )
    #set( $symbol_dollar = '$' )
    #set( $symbol_escape = '\' )
    

那么我们是否可以在生成原型工程时,不仅仅是对 java、xml 文件进行值引用的解析,也可以对更多类型的文件进行值引用解析呢?配置 mvn archetype:create-from-project 的参数,再次执行生成原型工程命令:

mvn clean archetype:create-from-project
-Darchetype.filteredExtentions=java,xml,yaml,factories
-DarchetypeCatalog=local

查看一下生成的 archetype-metadata.xml

<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor name="maven-archetype-test">
    <fileSets>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" encoding="UTF-8">
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.factories</include>
                <include>**/*.yaml</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/test/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet encoding="UTF-8">
            <directory></directory>
            <includes>
                <include>.gitignore</include>
            </includes>
        </fileSet>
    </fileSets>
</archetype-descriptor>

archetype.properties

 archetype.properties

该文件用于设置 mvn archetype:create-from-project 的参数,不仅可以设置 filteredExtentions,还可以配置自定义属性,官方文档 。最终,会直接在元数据文件中生成参数相对应的配置。

# 内置参数
archetype.filteredExtensions=java,yml,yaml,xml,txt,groovy,cs,mdo,aj,jsp,gsp,vm,html,xhtml,properties,factories,.classpath,.project
# Velocity templates encoding (Default UTF-8)
archetype.encoding=UTF-8
# Archetype project output dic (Default path as below, cannot be modified)
# outputDirectory: ${project.build.directory}/generated-sources/archetype

# 自定义参数
extParam1=extParamValue1
extParam2=extParamValue2

自定义参数会被生成到元数据文件中的 <requiredProperty> 标签,以便模板文件中可以通过${}进行取值。标签中的 key 即配置项的 key,<requiredProperty> 的子标签 <defaultValue> 的值即为配置项值。

filteredExtensions 属性,则会在 fileSet 标签的 filtered 属性上设置 true 值

指定配置文件执行:

mvn clean archetype:create-from-project
-Darchetype.properties=archetype.properties
-DarchetypeCatalog=local

<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor name="maven-archetype-test">
    <requiredProperties>
        <requiredProperty key="extParam2">
            <defaultValue>extParamValue2</defaultValue>
        </requiredProperty>
        <requiredProperty key="extParam1">
            <defaultValue>extParamValue1</defaultValue>
        </requiredProperty>
    </requiredProperties>
    <fileSets>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" encoding="UTF-8">
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.factories</include>
                <include>**/*.yaml</include>
            </includes>
        </fileSet>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
            <directory>src/test/java</directory>
            <includes>
                <include>**/*.java</include>
            </includes>
        </fileSet>
        <fileSet encoding="UTF-8">
            <directory></directory>
            <includes>
                <include>.gitignore</include>
            </includes>
        </fileSet>
    </fileSets>
</archetype-descriptor>

自定义属性值的二次设置

执行生成目标项目命令时,我们可以再次指定自定义参数值,覆盖掉元数据文件中配置的自定义参数的默认值。测试流程如下:

1. 生成原型工程

mvn clean archetype:create-from-project
-Darchetype.properties=archetype.properties
-DarchetypeCatalog=local

2. 生成目标项目

修正测试文件中的转义:

#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
server:
  port: 8080
  servlet:
    context-path: /
    session:
      timeout: PT1H

spring:
  profiles:
    active: dev
  application:
    # name: ${symbol_dollar}{extParam1} / ${symbol_dollar}{extParam2}
    name: ${extParam1} / ${extParam2}

3. 生成目标项目

mvn archetype:generate
-DinteractiveMode=false
-DarchetypeCatalog=local
-DarchetypeGroupId=priv.cqq
-DarchetypeArtifactId=maven-archetype-test-archetype
-DarchetypeVersion=0.0.1-SNAPSHOT
-DgroupId=org.cqq
-DartifactId=targetapp
-Dpackage=org.cqq.targetapp
-Dversion=0.0.1-SNAPSHOT
-DextParam1=extParamValue1-modified

目标项目文件内容如下:

最终测试文件输出

多模块

前面的案例都是在单模块项目中完成的。对于多模块,构建各个阶段工程的方式、最终的原型工程的使用,与单模块相比没有什么不同,因为构建命令:

mvn archetype:create-from-project

会帮助我们做好多模块的处理,将多模块的信息自动填写到元数据文件中,我们仅需构建好一个正确的多模块原型元工程即可。如下:

多模块原型元工程

生成原型工程后,元数据文件中生成的多模块信息描述:

<modules>
    <module id="${rootArtifactId}-common" dir="__rootArtifactId__-common" name="${rootArtifactId}-common">
        <fileSets>
            <fileSet filtered="true" packaged="true" encoding="UTF-8">
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.java</include>
                </includes>
            </fileSet>
            <fileSet filtered="true" encoding="UTF-8">
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.factories</include>
                </includes>
            </fileSet>
            <fileSet encoding="UTF-8">
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.lua</include>
                </includes>
            </fileSet>
        </fileSets>
    </module>
    <module id="${rootArtifactId}-service" dir="__rootArtifactId__-service" name="${rootArtifactId}-service">
        <fileSets>
            <fileSet filtered="true" packaged="true" encoding="UTF-8">
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.java</include>
                </includes>
            </fileSet>
        </fileSets>
    </module>
    <module id="${rootArtifactId}-web" dir="__rootArtifactId__-web" name="${rootArtifactId}-web">
        <fileSets>
            <fileSet filtered="true" packaged="true" encoding="UTF-8">
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.java</include>
                </includes>
            </fileSet>
            <fileSet filtered="true" encoding="UTF-8">
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.yaml</include>
                </includes>
            </fileSet>
        </fileSets>
    </module>
</modules>

原型工程的实际应用

1. 命令行

在前面已经说过了:

mvn archetype:generate -DinteractiveMode=false -DarchetypeCatalog=local
-DarchetypeGroupId=priv.cqq
-DarchetypeArtifactId=maven-archetype-test-archetype
-DarchetypeVersion=0.0.1-SNAPSHOT
-DgroupId=org.cqq -DartifactId=targetapp
-Dpackage=org.cqq.targetapp
-Dversion=0.0.1-SNAPSHOT
-DextParam1=extParamValue1-modified
-DextParam2=extParamValue2-modified

2. idea 导入

将原型成功 install 后,在 Archetype 项点击 add 按钮,输入已 install 的可运行原型工程的坐标信息:

idea使用archetype创建项目-1

添加后,在 Additional Properties 中就可以回显出所有的自定义参数,非常好用!

idea使用archetype创建项目-2
点击创建,生成的项目结构如下:

idea使用archetype创建项目-3

遇到的坑与经验总结

1. 自定义属性问题

1.1 多层级自定义属性无法被正确引用

多层级的自定属性是无法被解析后进行值替换的,比如:ext.server.port=8080

  1. 通过指定-Darchetype.properties文件的方式从原型元工程生成原型工程,多层级的自定义属性都不会生成 requiredProperty 标签在元数据文件中
  2. 即使手动在元数据文件中补充了多层级属性的 requiredProperty 标签,在通过可运行原型工程生成目标项目时,也无法通过 ${ext.server.port} 正确的引用到实际配置值
1.2 预先在元工程定义的值引用无法正确同步到原型工程

当我们在原型元工程中就定义好模板文件需要引用的单级自定义属性,并通过命令创建原型工程:

mvn clean archetype:create-from-project
-Darchetype.properties=archetype.properties

期望:

  1. 原型工程中的模板文件也同时定义好了自定义属性值引用
  2. 帮助我们生成 archetype-metadata.xml 文件

但,Velocity 解析的非常不智能!原型元工程中提前编写好的自定义属性值引用,经常无法正确的在原型工程中生成,常发生格式错乱。也就是说,最后还是需要校验一遍最终的模板文件后,在进行原型工程的 install

1.3 经验总结
  1. 若期望在原型元工程中就定义好自定义属性引用,并能更少发生解析错误的生成原型工程。经过多次尝试,单层级自定义属性名风格最好为大驼峰的风格,能够尽少的发生无法正确同步的问题
  2. 为什么一定要在原型元工程中就定义好自定义属性引用,这是因为后期可能会经常改动模板,以至于我们需要多次的重新构建出原型工程。如果不再原型元工程中提前编写好自定义属性引用,那么每次都需要整体改一遍新生成的原型工程
  3. 自定义属性最好加上特殊前缀:在原型工程中定义的自定属性引用:${attr},在通过命令生成原型工程时,会被解析为 ${symbol_dollar}{attr}。为了自定义原型属性可以生效,我们需要剔除 {symbol_dollar} 这部分,这个在前面说过了,否则不会正确完成值引用解析,会被原样输出到结果代码文件中。除此之外,项目中原本的 ${attr} 引用也会被转译,为了区分是原型自定义属性引用还是原生属性引用,我们可以通过设置前缀的方式,快速通过编辑器进行内容替换:保留转译后的原生属性引用的同时,将原型自定义属性引用改为 ${attr} 的格式。
  4. requiredProperty 标签是否需要默认值:个人更倾向不给默认值,在构建目标项目时手动传入。
  5. ${package} 的自动替换:在原型元工程中所有的 Java 文件间的引用,都会在生成原型工程时被转为 ${package}。如下例:priv.cqq.multimodulebackend 就会被转为 ${package}。进而使得生成目标项目时,元工程文件间的引用都可以正确的被替换为目标项目文件间的引用。
    @Pointcut("execution(*priv.cqq.multimodulebackend.web.controller..*Controller.*(..))")
    private void log() {
    }
    
    总结下来即:在编写原型元工程时无需考虑包路径问题,最终都可以被正确的转为目标项目的包路径。

2. 升级到新版本 maven-archetype 插件遇到的问题

由 2.0-alpha-4 远古版本升级到 2021 年最新发布的 3.2.1 版本:

<plugin>

    <!-- <groupId>org.apache.maven.plugins</groupId> -->
    <!-- <artifactId>maven-archetype-plugin</artifactId> -->
    <!-- <version>2.0-alpha-4</version> -->

    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-archetype-plugin</artifactId>
    <version>3.2.1</version>
</plugin>
2.1 .m2/settings.xml 文件不存在

执行生成原型工程命令失败:

mvn clean archetype:create-from-project
-Darchetype.properties=archetype.properties
-DarchetypeCatalog=local

[WARN] Maven will be executed in interactive mode, but no input stream has been configured for this MavenInvoker instance.
[ERROR] Error executing Maven.
[ERROR] The specified user settings file does not exist: /Users/congqingquan/.m2/settings.xml
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for multi-module-backend 1.0-SNAPSHOT:
[INFO] 
[INFO] multi-module-backend ............................... FAILURE [  3.155 s]
[INFO] multi-module-backend-common ........................ SUCCESS [  0.003 s]
[INFO] multi-module-backend-domain ........................ SUCCESS [  0.003 s]
[INFO] multi-module-backend-dao ........................... SUCCESS [  0.003 s]
[INFO] multi-module-backend-manager ....................... SUCCESS [  0.003 s]
[INFO] multi-module-backend-integration ................... SUCCESS [  0.005 s]
[INFO] multi-module-backend-service ....................... SUCCESS [  0.003 s]
[INFO] multi-module-backend-web ........................... SUCCESS [  0.004 s]
[INFO] multi-module-backend-mq ............................ SUCCESS [  0.004 s]
[INFO] multi-module-backend-start ......................... SUCCESS [  0.004 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.746 s
[INFO] Finished at: 2024-03-03T02:08:04+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:3.2.1:create-from-project (default-cli) on project multi-module-backend: Invoker process ended with result different than 0! -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

提示 The specified user settings file does not exist: /Users/congqingquan/.m2/settings.xml

很明显这是在寻找默认的 maven settings 文件。由于我们使用的是自己的 maven,那么指定一下个人 maven settings 配置文件的位置就好了。追加 -s 参数:

mvn clean archetype:create-from-project
-Darchetype.properties=archetype.properties
-DarchetypeCatalog=local
-s /Users/congqingquan/development/maven/apache-maven-3.6.3/conf/settings.xml

3. 多模块原型元工程的包路径替换问题

前面提到:原型元工程中的包层级会被剔除,切换到了根路径。后面根据可运行的原型工程生成目标项目时,可动态切换为用户输入的设置路径。在多模块元工程中,这个是有限制的,只能剔除两级。很坑!

元工程中的结构:

多模块原型元工程的包路径替换问题-1

生成的原型工程结构(pck 并没有被剔除):

多模块原型元工程的包路径替换问题-2

所以为了目标工程的包路径可以完全根据输入而定,元工程中的代码结构,最多保留两级。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值