手把手教你玩maven脚手架

2021/11/29 1634点热度 0人点赞 0条评论

生成脚手架

随着新框架的不断稳定(同时也带来了不错的收益),新的项目以及重构项目不断的往新框架上切,基于这个原因,要把新框架整一个脚手架。

脚手架中包含了demo(为了学习而框架,实际开发中会有一些便利性的调整)

注意事项

  • 依赖maven环境,必须配置MVAVA_HOME

  • 依赖jdk环境(一定要jdk,不要jre)

  • maven-archetype 的模板使用velocity

引入插件以及自定义配置文件

先找一个可以跑起来的demo,在pom文件中引入脚手架的maven plugin 我的工程结构如下:

项目地址:https://github.com/yxkong/ddd-framework/tree/main/ddd-demo

基于ddd应用框架写的demo

  • adapter 适配器层

  • application 应用层

  • domain 领域层

  • infrastructure 基础设施层

我们的项目结构

在demo的pom文件中引入

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-archetype-plugin</artifactId>
    </plugin>

maven-archetype-plugin工作流程如下: 具体使用请看这里: http://maven.apache.org/archetype/maven-archetype-plugin/

自定义 archetype.properties

package=com.yxkong.demo
version=1.0
groupId=com.yxkong
artifactId=ddd-demo
# 需要忽略的文件,不会打包进去
excludePatterns=/**/*.iml,/.idea/**/*,/logs,/target
# 需要采用template解析和替换本配置文件定义的参数的文件
archetype.filteredExtensions=java,sql,yml,xml,properties,factories,ftl
# 自定义参数,自定义的参数在模板中可以使用
appName=demo
upperAppName=Demo
webPort=8081
author=yxkong

命令生成脚手架

在ddd-demo项目中执行以下命令

指定setting(我本地使用了两套配置,所以我通过-s指定指定了)

mvn clean archetype:create-from-project -Darchetype.properties=archetype.properties  -s /Users/yxk/javaServer/maven/apache-maven-3.6.3/conf/settings-aliyun.xml

指定以archetype.properties

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

如果没有自定义参数可以直接执行以下命令,都是使用的默认mvn

mvn archetype:create-from-project

执行完命令后,会在target目录下生成generated-sources目录资源

脚手架模板处理

新建一个空项目ddd-archetype,将target目录中generated-sources 拷贝进去

基本框架修改

修改成自己想定义的框架的groupId、artifactId、version,在生成代码时引用的archetype*相关参数都是从这里来的。

ddd-archetype/pom.xml

  <groupId>com.yxkong</groupId>
  <artifactId>ddd-archetype</artifactId>
  <version>0.1</version>
  <packaging>maven-archetype</packaging>

  <name>ddd-archetype</name>

自定义属性 custom.properties

#set( $appName = ${package} )
#if(${package.indexOf(".")}!=-1)
    #set( $cc = ${package.split(".")})
    #if($cc != $null && ${cc.size()} > 0)
        #set( $lastIndex = $cc.size() - 1 )
        #set( $$appName = $cc[$lastIndex] )
    #end
#end
#set( $upperAppName = ${appName}.substring(0).toUpperCase() )

个性化修改

要想个性化修改,我们必须知道一些基本规则:

在文件中通过${变量名} 去替换
在路径中通过__变量名__ 去替换
已有变量如下:
- 
- appName  应用名称首字母小写  通过 mvn archetype:generate 注入
- upperAppName 应用名称首字符大写  通过 mvn archetype:generate 注入
- webPort 应用启动端口  通过 mvn archetype:generate 注入
- author  author  通过 mvn archetype:generate 注入
- dbName  数据库名  通过 mvn archetype:generate 注入
- package 要生成的报名,通过 mvn archetype:generate 注入
- groupId 要生成的groupId 通过 mvn archetype:generate 注入
- artifactId 要生成的artifactId (每个模块中的不一样,依赖于mavne的结构)
- version 要生成的版本号 通过 mvn archetype:generate 注入
- rootArtifactId  parent中的 artifactId
- rootGroupId parent中的 artifactId 一般全局一样

在这里我一些需求,每次我生成的demo,需要加上模块前缀 比如:我生成一个user-ddd的项目,内部结构需要如下:

user-ddd
 - user-adapter
   - com.yxkong
      -  UserStater.java
   - com.yxkong.user.adapter
 - user-application
   - com.yxkong.user.application
 - user-domain
   - com.yxkong.user.domain
 - user-infrastructure
   - com.yxkong.user.infrastructure

修改的地方:

  • 模块名前面都加__appName__-模块名

  • 模块内都是 package.模块名

  • 类中的引用都用package替换

重点是这里 src/main/resources/META-INF/maven/archetype-metadata.xml

  <modules>
    # dir 要注意,一定要和项目中的真实目录一样(变量不会解析),在生成时,会去对应的目录里copy模板,然后生成新的项目
    # id 和 name都是要生成的模块名称
    <module id="${appName}-adapter" dir="__appName__-adapter" name="${appName}-adapter">

 </modules>

生成archetype-catalog 与上传

所有的地方调整完以后执行,在该项目中执行

mvn clean install

会在本地的仓库中生产archetype-catalog.xml,内容就是ddd-archetype/pom.xml 中定义的

<?xml version="1.0" encoding="UTF-8"?>
<archetype-catalog xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0 http://maven.apache.org/xsd/archetype-catalog-1.0.0.xsd"
    xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <archetypes>
    <archetype>
      <groupId>com.yxkong</groupId>
      <artifactId>ddd-archetype</artifactId>
      <version>0.1</version>
      <description>Parent pom providing dependency and plugin management for applications built with Maven</description>
    </archetype>
  </archetypes>
</archetype-catalog>

上传远程服务器

mvn deploy

通过脚手架生成项目

找一个空目录执行以下命令,切记、切记、切记

mvn archetype:generate  -DarchetypeGroupId=com.yxkong -DarchetypeArtifactId=ddd-archetype -DarchetypeVersion=0.1 -DgroupId=com.yxkong -DartifactId=user-ddd -Dpackage=com.yxkong.user  -Dversion=1.0  -DappName=user -DupperAppName=User -X  -DarchetypeCatelog=local -s /Users/yxk/javaServer/maven/apache-maven-3.6.3/conf/settings-aliyun.xml

如果报以下错误:说明执行 generate命令的时候当前目录有一个pom文件

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:3.2.0:generate (default-cli) on project ddd-archetype: Unable to add module to the current project as it is not of packaging type 'pom' -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:3.2.0:generate (default-cli) on project ddd-archetype: Unable to add module to the current project as it is not of packaging type 'pom'

生成后的项目目录如下:

修改后的archetype-metadata.xml

<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor xsi:schemaLocation="https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0 http://maven.apache.org/xsd/archetype-descriptor-1.1.0.xsd" name="ddd-archetype"
    xmlns="https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <requiredProperties>
    <requiredProperty key="appName">
      <defaultValue>demo</defaultValue>
    </requiredProperty>
    <requiredProperty key="upperAppName">
      <defaultValue>Demo</defaultValue>
    </requiredProperty>
    <requiredProperty key="author">
      <defaultValue>yxkong</defaultValue>
    </requiredProperty>
    <requiredProperty key="webPort">
      <defaultValue>8001</defaultValue>
    </requiredProperty>
    <requiredProperty key="dbName">
      <defaultValue>user</defaultValue>
    </requiredProperty>
  </requiredProperties>
  <fileSets>
    <fileSet filtered="true" encoding="UTF-8">
      <directory>doc/init</directory>
      <includes>
        <include>**/*.sql</include>
      </includes>
    </fileSet>
    <fileSet filtered="true" encoding="UTF-8">
      <directory>doc</directory>
      <includes>
        <include>**/*.yml</include>
      </includes>
    </fileSet>
    <fileSet filtered="true" encoding="UTF-8">
      <directory>doc/nacos</directory>
      <includes>
        <include>**/*.properties</include>
      </includes>
    </fileSet>
    <fileSet encoding="UTF-8">
      <directory>doc/conf</directory>
      <includes>
        <include>**/*.log</include>
        <include>**/*.cnf</include>
        <include>**/*.conf</include>
      </includes>
    </fileSet>
    <fileSet encoding="UTF-8">
      <directory>ssh</directory>
      <includes>
        <include>**/*.sh</include>
      </includes>
    </fileSet>
    <fileSet encoding="UTF-8">
      <directory>doc</directory>
      <includes>
        <include>**/*.png</include>
      </includes>
    </fileSet>
    <fileSet encoding="UTF-8">
      <directory>doc/nacos</directory>
      <includes>
        <include>**/*.env</include>
      </includes>
    </fileSet>
    <fileSet encoding="UTF-8">
      <directory></directory>
      <includes>
        <include>readme.md</include>
        <include>.gitignore</include>
      </includes>
    </fileSet>
  </fileSets>
  <modules>
    <!--
     dir 是真实的目录,是什么就是什么,不会解析,在生成的时候从这个目录里获取模板
     id 和name 都是要生成的模块名称
    -->
    <module id="${appName}-adapter" dir="__appName__-adapter" name="${appName}-adapter">
      <fileSets>
        <!-- 不要加packaged="true" ,加了会在对应的directory中添加package包-->
        <fileSet filtered="true" encoding="UTF-8">
          <directory>src/main/java</directory>
          <includes>
            <include>**/*.java</include>
            <include>**/*.gitkeep</include>
          </includes>
        </fileSet>
        <fileSet filtered="true" encoding="UTF-8">
          <directory>src/main/resources</directory>
          <includes>
            <include>**/*.yml</include>
          </includes>
        </fileSet>
        <fileSet encoding="UTF-8">
          <directory></directory>
          <includes>
            <include>.gitignore</include>
          </includes>
        </fileSet>
      </fileSets>
    </module>
    <module id="${appName}-application" dir="__appName__-application" name="${appName}-application">
      <fileSets>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
          <directory>src/main/java</directory>
          <includes>
            <include>**/*.java</include>
            <include>**/*.gitkeep</include>
          </includes>
        </fileSet>
        <fileSet encoding="UTF-8">
          <directory></directory>
          <includes>
            <include>.gitignore</include>
          </includes>
        </fileSet>
      </fileSets>
    </module>
    <module id="${appName}-infrastructure" dir="__appName__-infrastructure" name="${appName}-infrastructure">
      <fileSets>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
          <directory>src/main/java</directory>
          <includes>
            <include>**/*.java</include>
            <include>**/*.gitkeep</include>
          </includes>
        </fileSet>
        <fileSet filtered="true" encoding="UTF-8">
          <directory>src/main/resources</directory>
          <includes>
            <include>**/*.yml</include>
            <include>**/*.xml</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>
    </module>
    <module id="${appName}-domain" dir="__appName__-domain" name="${appName}-domain">
      <fileSets>
        <fileSet filtered="true" packaged="true" encoding="UTF-8">
          <directory>src/main/java</directory>
          <includes>
            <include>**/*.java</include>
            <include>**/*.gitkeep</include>
          </includes>
        </fileSet>
        <fileSet encoding="UTF-8">
          <directory></directory>
          <includes>
            <include>.gitignore</include>
          </includes>
        </fileSet>
      </fileSets>
    </module>
  </modules>
</archetype-descriptor>

src/main/resources/archetype-resources/pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.yxkong</groupId>
        <artifactId>ddd-parent</artifactId>
        <version>0.1</version>
    </parent>
    <artifactId>${artifactId}</artifactId>
    <groupId>${groupId}</groupId>
    <packaging>pom</packaging>
    <version>${version}</version>
    <modules>
        <module>${appName}-adapter</module>
        <module>${appName}-application</module>
        <module>${appName}-infrastructure</module>
        <module>${appName}-domain</module>
    </modules>
    <properties>
        <argLine>-Dfile.encoding=UTF-8</argLine>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>${groupId}</groupId>
                <artifactId>${appName}-adapter</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>${groupId}</groupId>
                <artifactId>${appName}-application</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>${groupId}</groupId>
                <artifactId>${appName}-infrastructure</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>${groupId}</groupId>
                <artifactId>${appName}-domain</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 配置远程仓库 -->
    <distributionManagement>
        <repository>
            <id>aliyun</id>
            <url>https://maven.aliyun.com/nexus/content/groups/public/</url>
        </repository>
    </distributionManagement>
    <repositories>
        <repository>
            <id>aliyun-public</id>
            <name>aliyun-public</name>
            <url>https://maven.aliyun.com/nexus/content/groups/public/</url>
        </repository>
    </repositories>

    <build>
        <defaultGoal>compile</defaultGoal>
        <finalName>${project.artifactId}-${project.version}</finalName>
        <plugins>
            <!-- 编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <!--屏蔽test-->
                    <skip>true</skip>
                    <!--传递额外的编译参数-->
                    <compilerArgs>
                        <arg>-verbose</arg>
<!--                        <arg>-Xlint:all,-options,-path</arg>-->
                        <arg>-Xlint:unchecked</arg>
                        <arg>-Xlint:deprecation</arg>
                        <!--添加一些jar,让bootstrap ClassLoader加载-->
<!--                        <arg>-Xbootclasspath:${java.home}/lib/rt.jar${path.separator}${java.home}/lib/jce.jar</arg>-->
                        <!--解决本地jar包依赖问题-->
<!--                        <arg>-Xextdirs:${basedir}\lib</arg>-->
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
            <!--防止将.gitignore文件忽略-->
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <addDefaultExcludes>false</addDefaultExcludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-archetype-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

http://maven.apache.org/archetype/maven-archetype-plugin/

mvn archetype:generate 参数说明: 

-DarchetypeGroupId=com.yxkong   脚手架的groupId
-DarchetypeArtifactId=ddd-archetype  脚手架的artifactId
-DarchetypeVersion=0.1 脚手架版本
-DgroupId=com.xxx 需要生成的项目groupId
-DartifactId=demo-archetype-generate 需要生成的项目artifactId
-Dversion=1.0.0 需要生成的版本号
-DarchetypeCatalog=internal 使用私有仓库脚手架jar包, 前提:已经把脚手架发布到私有仓库中

  remote,远程Maven库中提供的模板。mvn archetype:generate默认使用该类模板
  local,本地Maven库中提供的模板。mvn archetype:generate默认使用该类模板,作为remote的补充。Maven初始为空,执行mvn install时会将当前项目加入local模板库
  internal,Apache Maven项目默认提供的模板。mvn archetype:generate -DarchetypeCatalog=internal使用该类模板
  file://...,给出本地计算机上的一个路径,在该路径下有一个archetype-catalog.xml文件(如果是其他文件名则必须给出),其中配置了模板

-DarchetypeCatalog=local 使用本地脚手架jar包, 如果不配置, 它会到中央仓库去下载, 会导致失败
-X debug模式

整体代码:

https://github.com/yxkong/ddd-framework

在用的过程中可以参考下面

docker-compose安装开发环境

将springboot项目构建为docker镜像

yxkong

这个人很懒,什么都没留下