現學現用,寫個Maven插件用下
Maven 插件實踐
上一次簡單介紹了如何編寫一個Maven插件,并且如何將插件的執行與Maven生命周期綁定,這樣通過調用maven生命周期方法時,則會在配置的階段按照插件的目標來執行代碼。
今天通過一個具體的插件來熟悉在項目中的使用。
一般公司的項目結構或者代碼結構都是非常固定的,有一些框架針對這種固化的代碼結構或約定的規范,在開發前會嚴格對項目進行模塊劃分,對各個模塊的代碼結構也會嚴格要求。那么我們則可以根據這種約定的規范,通過工具來自動化的生成代碼,從而減少開發人員的工作量。
示例項目
比如我們的項目一般都會由多個模塊組成,比如下面的示例:
DMP
├ system
│ ├ account
│ ├ entity
│ ├ dao
│ └ service
│ └ web
│ ├ role
│ ├ ...
│ ├ permission
│ ├ ...
│ └ pom.xml
├ monitor
│ ├ database
│ ├ ...
│ ├ disk
│ ├ ...
│ ├ memory
│ ├ ...
│ └ pom.xml
└ pom.xml
在上面的例子中,我們項目包括了system、monitor等多個模塊,其中system模塊包含了account、role、permission三個子模塊, monitor模塊包含了database、disk、memory三個子模塊,每個子模塊又包含了特定的代碼結構. 這個屬于我們約定的模塊劃分規則,那么基于這樣的規則,我們完全可以通過開發一個插件來自動生成這種約定結構的空項目。
實現步驟
假設插件名稱為 `module-create-maven-plugin`,將來我們會通過該插件實現
項目模塊文件夾和一些通用文件的自動生成。插件大概配置如下:
<build>
<plugins>
<plugin>
<groupId>com.sucls.blog.plugin</groupId>
<artifactId>module-create-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<basedir>E:\\_projects\\demo\\DMP</basedir>
<modules>
<module>system/account</module>
<module>system/role</module>
<module>monitor/databse</module>
<module>monitor/disk</module>
<module>monitor/memory</module>
</modules>
</configuration>
</plugin>
</plugins>
</build>
根據我們預期要求,來設想插件的開發過程。
定義一個maven插件項目
創建一個maven插件項目,在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>
<groupId>com.sucls.blog.plugin</groupId>
<artifactId>module-create-plugin</artifactId>
<version>1.0.0</version>
<packaging>maven-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.8.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
</dependencies>
</project>
創建一個Mojo根據配置添加實現邏輯
goal的名字設計成modules,由于最終項目是基于maven構建,所以會生成pom.xml,插件中的幾個參數就是為了生成pom而設計
@Mojo(name = "run")
public class ModulesCreatePlugin extends AbstractMojo {
@Parameter(property = "basedir",defaultValue = "${project.basedir}")
private String basedir;
@Parameter
private String project;
@Parameter(property = "groupId",defaultValue = "${project.groupId}")
private String groupId;
@Parameter(property = "artifactId",defaultValue = "${project.artifactId}")
private String artifactId;
@Parameter(property = "version",defaultValue = "${project.version}")
private String version;
@Parameter
private List<String> modules;
private ModuleTemplateHelper moduleTemplateHelper;
public ModulesCreatePlugin(){
init();
}
public void init(){
moduleTemplateHelper = new ModuleTemplateHelper();
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
Log log = getLog();
log.info(StringUtils.repeat("=",50));
log.info(modules.toString());
createModules();
log.info(StringUtils.repeat("=",50));
}
private void createModules() {
if(modules != null && modules.size() >0){
List<File> moduleFiles = new ArrayList<>();
modules.forEach(module -> {
// 創建目錄
File path = new File(basedir,module);
path.mkdirs();
moduleFiles.add(path);
});
Set<String> parentModules = new HashSet<>();
// 添加pom.xml
for (File module : moduleFiles) {
File parent = module.getParentFile();
parentModules.add(parent.getName());
// 上級pom
if( !new File(parent,"pom.xml").exists() ){
moduleTemplateHelper.process("pom.ftl", new ModuleEntity(groupId,project,version, parent.getName()),parent.getAbsolutePath()+"/pom.xml");
}
// 追繳module
XmlUtils.appendModule(new File(parent,"pom.xml"), module.getName());
// 模塊pom
moduleTemplateHelper.process("jar.ftl", new ModuleEntity(groupId,parent.getName(),version,module.getName()),module.getAbsolutePath()+"/pom.xml");
new File(module,"src/main/java").mkdirs();
new File(module,"src/main/resources").mkdirs();
}
// 項目pom.xml追加module
if(new File(basedir,"pom.xml").exists()){
for (String parentModule : parentModules) {
XmlUtils.appendModule(new File(basedir,"pom.xml"), parentModule);
}
}
}
}
}
上面我們通過ModuleTemplateHelper輔助類結合freemaker框架,最后為各個模塊生成對應的pom.xml文件;通過自定義的XmlUtils工具類結合JDK Documentation API,實現父級模塊中modules節點的添加子module;
public class ModuleTemplateHelper {
private Configuration configuration;
public ModuleTemplateHelper() throws IOException {
configuration = new Configuration(Configuration.VERSION_2_3_22);
configuration.setTemplateLoader(new ClassTemplateLoader(this.getClass(), "/templates"));
configuration.setDefaultEncoding("UTF-8");
}
public void process(String tpl, Object module, String outputPath){
Template template = configuration.getTemplate(tpl);
template.process(module, new FileWriter(outputPath));
}
}
public class XmlUtils {
static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
static TransformerFactory transformerFactory = TransformerFactory.newInstance();
static DocumentBuilder documentBuilder;
static {
try {
documentBuilder = documentBuilderFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
}
}
/**
*
* @param pomXmlPath
* @param moduleName
*/
public static void appendModule(File pomXml, String moduleName) {
try {
Document document = documentBuilder.parse(pomXml);
NodeList modules = document.getElementsByTagName("modules");
Node modulesNode = null;
if( modules.getLength()>0 ){
modulesNode = modules.item(0);
}
if( modulesNode == null ){
modulesNode = document.createElement("modules");
document.appendChild(modulesNode);
}
// 追加
Element module = document.createElement("module");
module.setTextContent(moduleName);
modulesNode.appendChild(module);
// 保存
transformerFactory.newTransformer().transform(new DOMSource(document), new StreamResult(pomXml));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
生成插件
執行下面的命令即可生成插件jar,并安裝到本地倉庫
mvn clean package
在項目中調用插件
在項目中引入插件,并且按照需要的模塊添加配置,最后在IDEA右側則可以看到該插件,雙擊運行,最終項目結構如下圖:
結束語
很多技術本身不復雜,合理使用與加工則可以將我們平時工作中重復相似的工作內容進行簡化,很多自動化工具亦是如此,只不過這些工作由別人完成了。通過今天的示例,主要了解如何將學到的知識具體化到工作中。