Maven插件初体验【附源码】

B站视频地址


学过Java的小伙伴对Maven一定很熟悉了,但对于Maven除了用来进行版本管理之外,你还用它做过什么呢?

或许很多人和我一样,用了几年的Maven压根就没想过Maven除了版本管理还可以做其它事情。


一、场景

场景一

比如你打包的时候需要修改某个包的名字,现在你需要把这个包版本从 1.0.0 改到1.0.1,如果只有几个服务你可以手动改,但如果有几十个服务就没办法手动改了,我们可以写一个 jib插件,在打包的时候自动去修改包名。


场景二

mabatis-generation 大家应该都用过,就是一个代码生成器,可以根据数据库里面的表结构生成固定代码,现在我们也有一个这样的需求,但是生成的代码结构有些不同,我们可以利用maven来实现这一功能。

最终目的就是输入一行命令,然后自动生成代码,提交到 git仓库,deploy 到maven仓库。

mvn business-generation:business-generation -DautoType=client -DtableName=xdx_test_one

二、代码实现

我们来用代码实现上面的场景二,下图是代码目录结构

在这里插入图片描述


2-1、技术实现

所谓的插件也无非是来执行我们的脚本,maven 插件也提供了一个入口 我们继承 AbstractMojo 重写 execute 方法,当运行这个插件的时候,就会去执行这个方法。

既然是根据数据库表来生成代码,那一定是少不了JDBC驱动了。

我们可以基于 字符串拼接 来生成我们想要的代码,但那太麻烦了,我们可以使用 freemarker 来生成我们想要的代码,更优雅,更具维护性。


2-2、插件入口

其实就是继承 AbstractMojo 重写execute方法

获取maven参数命令

mvn clean test -P test -DautoType=client
 // 可以通过获取
System.getProperty("autoType");

import cn.ideamake.excuter.ClientExcuter;
import cn.ideamake.excuter.ServiceExcuter;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

@Mojo(name = "business-generation")
@SuppressWarnings("unused")
public class WriteTextFilesMojo extends AbstractMojo {

    @Parameter(defaultValue = "UTF-8")
    @SuppressWarnings("unused")
    private String charset;

    // 读取 pom文件里面的配置
    @Parameter
    private String url;

    // 读取 pom文件里面的配置
    @Parameter
    private String userName;

    // 读取 pom文件里面的配置
    @Parameter
    private String password;

    @Override
    public void execute()  {

        // 获取 maven 命令后面的参数
        String autoType = System.getProperty("autoType");
        String tableName = System.getProperty("tableName");

        try {
            if ("client".equals(autoType)) {
                new ClientExcuter().excute(tableName, url, userName, password);
            }else if ("service".equals(autoType)){
                new ServiceExcuter().excute(tableName, url, userName, password);
            }else {
                System.out.println("\r\n 暂不支持你想要的操作!");
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2-3、获取MySQL 表结构信息

获取某个库中的数据表

SELECT
    table_name
FROM
information_schema.tables
WHERE table_schema = #{dataBase} AND table_type = 'base table'

在这里插入图片描述


获取表结构信息

SELECT
    *
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = #{dataBase} and TABLE_NAME = #{tableName}

在这里插入图片描述

2-4、Java代码执行命令

我们既然需要自动提交、自动打包,那肯定是要使用脚本执行 git 和 maven 命令的。

在Java里面我们可以使用下面的代码来执行命令

Runtime.getRuntime().exec();

比如我们执行 git add . 那肯定是要在当前目录下去执行的。


在win下,我们可以通过获取当前目录

String pwd = IOUtil.toString(Runtime.getRuntime().exec(new String[]{"PowerShell","pwd"}).getInputStream())
         .replaceAll("\n", "")
         .replaceAll("\r", "")
         .replaceAll(" ", "")
         .replaceAll("----", "")
         .replaceAll("Path", "");
 System.out.println(pwd);

在mac下

pwd = IOUtil.toString(Runtime.getRuntime().exec("pwd").getInputStream())
        .replaceAll("\n", "")
        .replaceAll("\r", "");

exec 方法是可以传递多个命令的,它们会依次执行


2-5、 ClientExcuter

这其实就是我们要执行的脚本了,这里我通过jdbc获取数据库中表结构,然后基于 freemarker来生成文档,最后执行相关命令来提交代码。


import org.codehaus.plexus.util.IOUtil;

/**
 * 执行器的通用功能
 */
public interface Excuter {
    
    default void gitPush() throws Exception {

        System.out.println("-------------------- git代码提交");
        String pwd = IOUtil.toString(Runtime.getRuntime().exec("pwd").getInputStream()).replaceAll("\n", "").replaceAll("\r", "");
        System.out.println("当前路径:"+ pwd);
        String commit = "cd "+ pwd +" && git add . && git commit -m '自动生成代码' && git push";
        System.out.println(commit);
        Process exec = Runtime.getRuntime().exec(new String[]{"bash", "-c", commit});
        int i = exec.waitFor();
        if (i == 0) {
            System.out.println("-------------------- git代码提交完毕");
        }else {
            System.out.println("-------------------- git代码提交异常");
            System.out.println(IOUtil.toString(exec.getErrorStream()));
        }
        exec.destroy();
    }
}

import cn.ideamake.util.JdbcUtils;
import cn.ideamake.util.StringUtils;
import cn.ideamake.util.TemplateUtils;
import org.codehaus.plexus.util.IOUtil;

import java.io.File;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 客户端的执行器
 */
public class ClientExcuter implements Excuter{


    public void excute(String table_name, String url, String userName, String password) throws Exception {
        System.out.println("SDK客户端自动生成开始:" + table_name);

        System.out.println("-------------------- 开始生成DTO");
        String tableName = StringUtils.underlineToHump(table_name, true);
        PreparedStatement stmt = JdbcUtils.getStatement(url, userName, password, "SELECT * FROM information_schema.COLUMNS WHERE TABLE_NAME = 'yxxx'");
        Map<String, String> dataBaseParams = generationDto(stmt, table_name, tableName);
        System.out.println("DTO生成完毕 " + tableName  + "DTO.java");
        JdbcUtils.closeJdbc(stmt);

        String dataBase = dataBaseParams.get("dataBase");
        System.out.println("-------------------- 开始生成FeignClient");
        generationFeignClient(dataBase, tableName, table_name);
        System.out.println("FeignClient 生成完毕!");

        gitPush();

        Runtime.getRuntime().exec("mvn install");
        System.out.println("-------------------- maven install、完成");
    }


    public Map<String, String> generationDto(PreparedStatement stmt, String table_name, String tableName) throws SQLException {
        Map<String, String> result = new HashMap<>();
        stmt.setString(1, table_name);
        ResultSet resultSet = stmt.executeQuery();
        List<Map<String, Object>> list = new ArrayList<>(15);
        while (resultSet.next()) {
            Map<String, Object> map = new HashMap<>();
            map.put("column", StringUtils.underlineToHump(resultSet.getString("COLUMN_NAME"), false));
            map.put("dataType", JdbcUtils.typeChange(resultSet.getString("DATA_TYPE")));
            map.put("desc", resultSet.getString("COLUMN_COMMENT"));
            list.add(map);

            result.put("dataBase", resultSet.getString("TABLE_SCHEMA"));
        }

        // 生成模板
        Map<String, Object> root = new HashMap<>();
        root.put("className", tableName);
        root.put("packageName", "cn.ideamake.sdk.base.dto.query");
        root.put("date", "2022-01-10");
        root.put("columns", list);
        File docFile = new File("base-sdk/src/main/java/cn/ideamake/sdk/base/dto/query/" + tableName + "DTO.java");

        TemplateUtils.generation(root ,docFile,"client/dto.ftl");

        return result;
    }


    public void generationFeignClient(String dataBase,String tableName, String table_name) {

        Map<String, Object> root = new HashMap<>();
        root.put("className", tableName);
        root.put("class_name", table_name);
        root.put("packageName", "cn.ideamake.sdk.base.client");
        root.put("date", "2022-01-10");
        root.put("data_base", dataBase);

        File docFile = new File("base-sdk/src/main/java/cn/ideamake/sdk/base/client/" +tableName + "FeignClient.java");
        TemplateUtils.generation(root ,docFile,"client/feignClient.ftl");
    }
}

2-6、freeMarker

这是一个模板引擎,可以用它来生成各色的代码,这个不是重点,而且也比较简单

封装的模板工具

import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.util.Map;

public class TemplateUtils {


    public static void generation(Map<String, Object> root, File targetFile,String templateSrc) {

        writeFile(root, targetFile ,templateSrc);
    }

    private static void writeFile(Map<String, Object> root, File targetFile,String templateSrc) {
        try {
            // 删除文件
            targetFile.delete();
            if (!targetFile.getParentFile().exists()) {
                System.out.println("not exists");
                //创建上级目录
                targetFile.getParentFile().mkdirs();
            }
            Configuration configuration = new Configuration();
            configuration.setDirectoryForTemplateLoading(new File("src/main/resources"));
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile)));
            Template template = configuration.getTemplate("template/" + templateSrc);
            template.process(root, out);

            if (null != out) {
                out.flush();
            }
        }catch (Exception e) {
            System.out.println("写文件异常!!!");
            e.printStackTrace();
        }
    }
}

模板是根据 ftl 文件来生成的,我们来看一个简单的模板

package ${packageName};

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
import java.util.Date;
/**
*  feign实体对象数据类
*
*  @author auto
*  @date ${date}
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class ${className}DTO {

<#list columns as attr>
    /**
     * ${attr.desc}
     */
    private ${attr.dataType} ${attr.column};

</#list>

三、使用

3-1、提交maven插件

直接 mvn install 安装到本地即可


3-2、引入 pom 文件

<plugin>
    <groupId>cn.ideamake</groupId>
    <artifactId>business-generation-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <configuration>
        <charset>UTF-8</charset>
        <url>jdbc:mysql://127.0.0.1:3306/xdx_blog?serverTimezone=Asia/Shanghai</url>
        <userName>root</userName>
        <password>123456</password>
    </configuration>
</plugin>

刷新后就可以看到我们的 插件了
在这里插入图片描述


3-3、引入模板文件

模板是从我们当前的 resources 文件夹下面读取的,所以我们需要把模板引入


3-4、执行命令

mvn business-generation:business-generation -DautoType=client -DtableName=xdx_test_one

这个命令有两个参数,一个是表示生成的方式(客户端),一个是要生成的表名。


错误

如果你执行命令后报错,可能是当前maven仓库地址不对,加个参数强制指定即可
在这里插入图片描述

mvn business-generation:business-generation -DautoType=clien2 -Dmaven.repo.local=D:\software\install\maven-3.8.1\apache-maven-3.8.1-my\maven-repository

四、Maven 插件执行周期

上面只是对maven插件的一个简单的应用,但基于此你已经明白了 maven插件的执行原理。

所谓的插件,无非就是 在什么时间、什么地点、做什么事情。 (mybatis的插件也是如此)

完整的生命周期参看这里


五、源码获取

关注微信公众号:小道仙97, 回复关键字获取:maven-generation

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值