开发一个maven脚手架

写在前面

开发新项目就需要搭建新工程,但是搭建新工程的这个过程是非常繁琐浪费时间的,并且不可避免的需要踩坑。更可怕的是,如果是在一个团队中,每新起一个项目都由不同的开发人员去自定义的搭建工程结构,那么对后续的统一管理,监控,运维简直是灾难。基于以上几点,团队内部其实是非常有必要搭建一个统一的脚手架来供统一使用

制作一个脚手架

下面我们就来详细的介绍如何搭建一个maven工程的脚手架

要搭建脚手架,首先我们需要一个模板工程,这个模板工程一般来说会集成一些工具类,底层中间件,通用配置,并且要有良好的分层结构等。需要能够达到开箱即用的程度。

以下是一个模板工程的目录结构,也是目前我们团队内部的标准化结构

.
├── example-client
│   ├── pom.xml
│   └── src
├── example-core
│   ├── pom.xml
│   └── src
├── example-server
│   ├── pom.xml
│   └── src
├── example-test
│   ├── pom.xml
│   └── src
├── Jenkinsfile
├── README.md
├── .gitignore
└── pom.xml

完成模板工程的搭建后,需要在模板工程的最外层 pom 文件加入以下配置

<build>  
    <pluginManagement>  
        <plugins>  
            <plugin>  
                <groupId>org.apache.maven.plugins</groupId>  
                <artifactId>maven-archetype-plugin</artifactId>  
                <version>3.0.1</version>  
            </plugin>  
            <plugin>  
                <groupId>org.apache.maven.plugins</groupId>  
                <artifactId>maven-compiler-plugin</artifactId>  
                <version>3.6.1</version>
                <configuration>  
                    <source>1.8</source>  
                    <target>1.8</target>  
                </configuration>  
            </plugin>  
            <plugin>  
                <groupId>org.apache.maven.plugins</groupId>  
                <artifactId>maven-resources-plugin</artifactId>  
                <version>3.0.2</version>
                <configuration>  
                    <encoding>UTF-8</encoding>  
                </configuration>  
            </plugin>  
        </plugins>  
    </pluginManagement>  
</build>

完成之后在根目录下执行

mvn archetype:create-from-project

刷新项目后,会发现在 ./target/generated-sources/archetype目录下生成了脚手架工程,生成的脚手架工程可以当成是一个独立的项目,目录结构如下图

.
├── pom.xml
├── src
│   ├── main
│   │   └── resources
│   │       ├── META-INF
│   │       │   └── maven
│   │       │       └── archetype-metadata.xml
│   │       └── archetype-resources
│   │           ├── Jenkinsfile
│   │           ├── README.md
│   │           ├── __artifactId__.iml
│   │           ├── __rootArtifactId__-client
│   │           │   ├── __parentArtifactId__-client.iml
│   │           │   ├── pom.xml
│   │           │   └── src
│   │           ├── __rootArtifactId__-core
│   │           │   ├── __parentArtifactId__-core.iml
│   │           │   ├── pom.xml
│   │           │   └── src
│   │           ├── __rootArtifactId__-server
│   │           │   ├── __parentArtifactId__-server.iml
│   │           │   ├── pom.xml
│   │           │   └── src
│   │           ├── __rootArtifactId__-test
│   │           │   ├── __parentArtifactId__-test.iml
│   │           │   ├── pom.xml
│   │           │   └── src
│   │           └── pom.xml
│   └── test
│       └── resources
│           └── projects
│               └── basic
│                   ├── archetype.properties
│                   └── goal.txt

在脚手架工程目录下执行 mvn install 就完成了脚手架的本地安装,安装完成之后,这个脚手架在本地就可以使用了
可以执行以下脚本来通过此脚手架创建项目

mvn archetype:generate -DgroupId=com.xxx.example -DartifactId=xxxx -Dpackage=com.xxx.example -DarchetypeGroupId=com.demo.archetype -DarchetypeArtifactId=demo-archetype -DarchetypeVersion=1.0.0-SNAPSHOT -DinteractiveMode=false

扫坑

不过如果我们直接这样使用的话,会发现生成了很多我们并不想要它出现的文件,比如 .idea .iml 文件等等,并且 .gitignore 文件也诡异的消失了(不知为何会忽略这个文件??)... 这显然不是成熟的脚手架了。那么就需要对它做一些额外的配置了

有两种方式可以解决上面出现的问题

  1. .gitignore文件重命名为 __gitignore__,然后在模板工程根目录下新建 archetype.properties 文件,并填入以下内容
## generate for archetype-metadata.xml
excludePatterns=archetype.properties,*.iml,.idea/,.idea/libraries,logs/,build.sh
## generate .gitignore file
gitignore=.gitignore

完成上述配置后,重新执行 mvn archetype:create-from-project 生成脚手架工程。再完成本地安装,上面出现的问题就会解决

  1. 第二种方式本质上和第一种方式是一样的,只是第二种方式是直接修改脚手架工程的配置文件。第一种方式相当于是执行 mvn archetype:create-from-project 时读取了 archetype.properties 帮我们做了配置文件的修改。

maven的脚手架工程下有两个重要的配置文件

  • ./src/test/resources/projects/basic/archetype.properties

    • 这里可以加入自定义变量,如 gitignore 变量
  • ./src/main/resources/META-INF/maven/archetype-metadata.xml

下面是一个典型的archetype-metadata.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor 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="example"
    xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <requiredProperties>
    <requiredProperty key="gitignore">
      <defaultValue>.gitignore</defaultValue>
    </requiredProperty>
    <requiredProperty key="port">
      <defaultValue>8888</defaultValue>
    </requiredProperty>
  </requiredProperties>
  <fileSets>
    <fileSet encoding="UTF-8" filtered="true">
      <directory></directory>
      <includes>
        <include>__gitignore__</include>
        <include>README.md</include>
        <include>Jenkinsfile</include>
        <include>pom.xml</include>
      </includes>
    </fileSet>
  </fileSets>
  <modules>
    <module id="${rootArtifactId}-client" dir="__rootArtifactId__-client" name="${rootArtifactId}-client">
      <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}-core" dir="__rootArtifactId__-core" name="${rootArtifactId}-core">
      <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>
      </fileSets>
    </module>
    <module id="${rootArtifactId}-server" dir="__rootArtifactId__-server" name="${rootArtifactId}-server">
      <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 filtered="true" encoding="UTF-8">
          <directory>src/main/resources/META-INF/dubbo</directory>
          <includes>
            <include>*</include>
          </includes>
        </fileSet>
        <fileSet filtered="true" encoding="UTF-8">
          <directory>src/main/resources</directory>
          <includes>
            <include>**/*.yml</include>
          </includes>
        </fileSet>
      </fileSets>
    </module>
    <module id="${rootArtifactId}-test" dir="__rootArtifactId__-test" name="${rootArtifactId}-test">
      <fileSets>
      </fileSets>
    </module>
  </modules>
</archetype-descriptor>

archetype-metadata.xml 结构

简单介绍archetype-metadata.xml文件的基本结构

  • <requiredProperties>是属性变量定义层,在这里定义的变量可以在archetype工程文件中通过 ${xxx} 来进行引用,文件名则可以通过 __xxx__来引用变量(这就是为什么要把.gitignore重命名为__gitignore__)。变量在执行 mvn archetype:generate时才录入真正内容
  • 再往下其实就是定义将要生成的工程目录结构,首先是一个 fileSets (包含多个 fileSet标签)标签定义了根目录需要生成哪些文件,再通过多个modules 标签来定义多个子模块(如果是多模块工程的话)。这其中比较重要的其实还是 fileSet标签,fileSet有两个重要的属性
    • filtered = true 如果为true,则该 fileSet包含的文件中的${} 占位符会被替换成相应的变量
    • packaged = true 如果为true,则 src/main/java 下得文件内容会被加入 指定包路径下

总结

一个优秀少坑的脚手架还是能极大的提升生产力的,毕竟这种重复且无价值的劳动我们还是交给工具吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容