最近公司要搭建一套新的平台,期间要创建多个工程,每创建一个工程都需要耗费大量的时间,包括各子模块的添加、jar的依赖等等,因为同一公司,同一项目代码风格基本一致,那么创建一个模板工程,自动生成代码,根据业务需要只需要将生成的代码稍微修改甚至不修改直接就编写业务代码的计划就提上日程了。而且使用模板生成代码还有一个好处就是代码风格一致。说干就干。
1.创建参照工程
既然是创建模板工程,那么首先生成的工程代码是什么样的,就要有一个参照,所以我们首先创建一个参照工程,就是我们最终要在项目中用到的工程结构是什么样的,此步骤是为了确定我们要建立的模板生成的代码都要有哪些模块,要有哪些常用的配置,要有哪些常用依赖等等。我是使用 interj idea来开发的,所以在IDE中创建一个多module的maven工程,过程不在赘述,结构如下图,(如果对项目工程比较熟悉或已经有现有的工程可以参考,那么可以忽略此步骤,直接进行步骤2)
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90TUhFDayIGasdUZ0Y0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DM2UzN0gzM1EDOygDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
展开看下细节
2.创建工程模板
有了参考之后,我们就要将参考的工程变成模板,以后用此模板生成的代码都和参考工程具有同样的结构、配置、依赖等等。模板也是一个maven工程,我们在IDE中新建一个工程test-archetype,创建如图所示的目录结构(注:必须包含用红框标注的目录结构且命名必须和图中示例一样,下面会逐个分析各目录结构的用途)
2.1 模板工程pom.xml
maven模板主要是用到了archetype插件,pom配置如下:
<?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>
<groupId>com.test</groupId>
<artifactId>test-archetype</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-archetype-plugin</artifactId>
<version>2.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2.2 archetype-resources目录
此目录是用来放置通过此模板商城的项目要有的文件,或者说是要生成的代码的镜像,也就是这个目录里放的是参照工程中的代码和配置文件,如图所示
但是并不是简单的将参照工程中的目录和文件拷贝过来,还要对目录和文件做一些修改,否则使用此模板生成的目录名称和代码的包路径都是固定的,并不会随着我们使用不同的ArtifactId而变化,下面我们说下如何修改。
对于模块(module)名和模块的目录命名,我们一般使用父工程的ArtifactId加上模块的功能来命名,例如商品系统,父工程的ArtifactId为product,其中api模块命名就是product-api等。那么在模板定义中如何来动态的创建子模块的目录呢,这时使用__rootArtifactId__占位符来动态获取父工程的ArtifactId,那么此时在archetype-resources目录下的api模块的目录名就不是参照工程中定义的archetype-reference-api,而是__rootArtifactId__-api了,最终archetype-resources目录下的结构如图
对于代码包路径目录的动态创建,在参照工程中Service模块,的启动类ServiceLauncher.java的目录结构是:工程所在目录\archetype-reference-service\src\main\java\com\test\archetype\reference\service\launcher,如图
其中模块(module)目录archetype-reference-service,通过上面说的使用占位符__rootArtifactId__我们已经可以动态生成了,那么下面的src\main\java\com\test\archetype\reference\service\launcher目录中尤其是com\test\archetype\reference这一段如何解决呢,总不能所有的项目中都要使用这个包路径结构吧,如果仔细看过代码我们能发现,在参照工程中所有代码的包路径结构都是已com\test\archetype\reference开始的,如果我们创建一个商品系统的代码,也应该是所有的代码结构都是已com\test\product开始,我们成这样的基础包结构为BasePackage,所有的子模块的包路径结构都应该是基础包路径加上模块包路径,录入商品系统api模块的包路径为com\test\product\api\xxx\xxx.......,那么在模板工程中,我们只创建基础包结构之后的目录结构,在参照工程中的ServiceLauncher的目录结构在模块工程中就变成了如图所示
说完了模块目录和包目录的动态,那么代码文件中的包名怎么动态呢,例如java文件中的import引用,spring配置文件中的类路径使用,sh脚步文件中的类路径使用等等,这些文件中使用的能动态配置吗,答案是可以。要使用变量。
对于代码中import或者spring、xml中引用类路径的,我们都使用自定义变量来替换,例如使用变量package,在代码中,在配置文件中,如下图所示
其他类型的替换一样,都是使用变量,例如pom文件中的信息,都是要动态生成的,一样使用变量
当然,这其中有些变量是系统支持的,有些变量是自定义的,例如rootArtifactId、project.version等都是系统支持的,有些是自动以的,例如上面用到的package,自定义的变量在使用模板的生成代码的时候要提供并复制,这个后面会细说。好了,到现在为止archetype-resources目录的作用以及该目录下的目录和文件如果动态定义以及说完了,那么现在可以生成模板了吗,当然还不行,要需要一个元数据文件,就是下面要降到的META-INF/maven目录下的archetype-metadata.xml文件了
2.3 archetype-metadata.xml
有了模板的镜像文件,这时候还要告诉怎么使用这些文件,这时就需要模板的元数据文件了,元数据文件定义如下:
<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor name="my-archetype">
<requiredProperties>
<requiredProperty key="groupId">
<defaultValue>com.test</defaultValue>
</requiredProperty>
<requiredProperty key="artifactId">
<defaultValue>test.archetype</defaultValue>
</requiredProperty>
<requiredProperty key="package">
<defaultValue>com.test.archetype</defaultValue>
</requiredProperty>
</requiredProperties>
<fileSets>
<fileSet filtered="true" encoding="UTF-8">
<directory></directory>
<includes>
<include>*.xml</include>
<include>*.properties</include>
</includes>
</fileSet>
</fileSets>
<modules>
<module id="${rootArtifactId}-api" dir="__rootArtifactId__-api" name="${rootArtifactId}-api">
<fileSets>
<fileSet filtered="true" encoding="UTF-8" packaged="true">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
<include>**/*.txt</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory></directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
</fileSets>
</module>
<module id="${rootArtifactId}-dao" dir="__rootArtifactId__-dao" name="${rootArtifactId}-dao">
<fileSets>
<fileSet filtered="true" encoding="UTF-8" packaged="true">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
<include>**/*.txt</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8" packaged="false">
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory></directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
</fileSets>
</module>
<module id="${rootArtifactId}-domain" dir="__rootArtifactId__-domain" name="${rootArtifactId}-domain">
<fileSets>
<fileSet filtered="true" encoding="UTF-8" packaged="true">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
<include>**/*.txt</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory></directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
</fileSets>
</module>
<module id="${rootArtifactId}-service" dir="__rootArtifactId__-service" name="${rootArtifactId}-service">
<fileSets>
<fileSet filtered="true" encoding="UTF-8" packaged="true">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
<include>**/*.txt</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8" packaged="false">
<directory>src/main/assemble</directory>
<includes>
<include>**/*.xml</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8" packaged="false">
<directory>src/main/bin</directory>
<includes>
<include>**/*.sh</include>
<include>**/*.bat</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8" packaged="false">
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory></directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
</fileSets>
</module>
<module id="${rootArtifactId}-web" dir="__rootArtifactId__-web" name="${rootArtifactId}-web">
<fileSets>
<fileSet filtered="true" encoding="UTF-8" packaged="true">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
<include>**/*.txt</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8" packaged="false">
<directory>src/main/webapp</directory>
<includes>
<include>**/*.*</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8" packaged="false">
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory></directory>
<includes>
<include>pom.xml</include>
</includes>
</fileSet>
</fileSets>
</module>
</modules>
</archetype-descriptor>
文件根节点的name用来定义此模板的名称
根节点下的fileSets用来定义包含archetype-resources目录下的哪些文件,directory为空表示这些文件要放在生成的父工程路径下面
modules节点用来定义生成的代码的模块列表
module节点用来定义模块详细信息,dir熟悉对应archetype-resources下的目录,name和id属性用来定义在生成的工程中的模块名
着重说下fileSet标签下的packaged和filtered熟悉
packaged属性用来标识,在生成代码时用不用创建包目录,例如上面我们的ServiceLauncher类的路径是src/main/java/service/launcher/ServiceLauncher.java,如果我们给定了package为com.xxx.product并且此属性是true,那么生成代码时会为我们创建com/xxx/product目录结构,ServiceLauncher类的路径就变成了src/main/java/com/xxx/product/service/launcher/ServiceLauncher.java
filtered属性用来标识,要不要在生成代码时,替换掉镜像文件中的变量,上面我们再代码中使用的package变量,如果给定了package为com.xxx.product并且此属性为true,那么文件中所有出现${package}的地方都会变成com.xxx.product
3 发布模板
好了,至此,模块已经定义好了,如果使用这个模板呢,在模块工程里,我们可以执行maven的install命令将模板装载到本地仓库,也可以使用maven的deploy命令,将模板发布到远程仓库,怎么通过deploy命令发布到远程仓库网上教程很多,就不说了。发布完只后如何用模板呢。
使用IDE创建工程时,已intelliJ IDEA为例,在新建工程时添加模板,填写模板的信息,(模板也是一个jar,跟普通jar依赖一样)
添加只后在模板列表中就能看到你定义的模板了,选择模板,下一步,填写新项目的groupId、artifactId等信息
继续下一步
不知道还有同学记得我吗上面用到了一个自定义的变量package吗,显然这里的properties定义中没有这个变量,那么这时候就要添加你在模板中用到的自定义变量了,添加后如图
好了,一直下一步,知道工程创建完成。
除了使用IDE添加模板之外,还可以使用脚步通过mvn的archetype:generate命令来实现代码创建,下面是我写的bat脚本,供大家参考,使用此脚本之前本地mvn环境要搭建好。
@echo off
echo 开始运行代码生成脚本,运行此脚本前请坚持本地maven是否已经配置
echo 请输入groupId:
set/p GROUP_ID=
echo 请输入artifactId:
set/p ARTIFACT_ID=
echo 请输入package:
set/p BASE_PACKAGE=
call mvn archetype:generate -DgroupId=%GROUP_ID% -DartifactId=%ARTIFACT_ID% -Dpackage=%BASE_PACKAGE% -Dversion="1.0.0" -DarchetypeGroupId=com.test -DarchetypeArtifactId=test-archetype -DarchetypeVersion=1.0.0-SNAPSHOT
pause