背景
九月份的最后一天抽空跟公司的java大神聊了会天,发现每天跟大神们聊天总能收获很多思路,想想接受了大神那么多的指点,就把他的一点小技巧分享出来。
这次介绍的是JAVA 工程自动通过工具生成标准工程,按照指定步骤生成整个项目工程,解决每次新建项目工程相互拷贝的问题,我也是在用了一次以后发现这个功能很好,就去找原作者请教了一下,因而有了这篇文章用以致敬原作者。
效果展示
我们使用的微服务是基于阿里开源的dubbo框架,整个项目的目录结构如下图所示。如下图中我们生成了一个promotion的dubbo项目工程,当然我们也可以生成其他额外的项目工程,只需要修改工程名字(如trade、member等)即可。只需执行命令:java -jar service-eve.jar app XXXX //xxxx 您的服务工程名字 比如:promotion member。
通过反编译service-eve.jar了解了下内部是如何实现上述功能的,先看下整个功能实现的目录结构。
1、项目工程目录
说明:
- ServiceEve.java文件作为入口函数解析命令
- template作为工程模板存在,内部是一个完整的dubbo工程项目
- appServiceName作为关键字后面由希望生成的工程命令进行替换
2、模板内部java类实现
说明:
- java类内部的实现会涉及到项目的package名称,所以需要进行替换
- 内部通过占位符${appServiceName}先占用,后期进行替换
- java类的占位符通过velocity模板进行替换
代码实现
public class ServiceEve
{
public static void main(String[] args)
throws IOException
{
if ((args == null) || (args.length < 2))
{
System.exit(0);
}
if (args[0].equals("app"))
{
genServiceTemplate(args[1]);
}
else
{
System.exit(0);
}
}
public static void genServiceTemplate(String appServcieName)
{
try
{
// 获取当前jar包的文件内容,因为整个模板在jar包的template目录当中
JarFile jar = new JarFile("service-eve.jar");
Enumeration<JarEntry> re = jar.entries();
// 遍历jar包中的所有文件,针对在template目录下的文件进行处理
while (re.hasMoreElements())
{
String name = ((JarEntry)re.nextElement()).getName();
if (name.contains("template/")) {
if (!name.endsWith("/"))
{
// 读取template目录下的文件,通过非/结尾来判断是否为文件
List<String> lines = IOUtils.readLines(ServiceEve.class.getClassLoader().getResourceAsStream(name), "UTF-8");
String targetFile = new File(".").getAbsolutePath() + "/" + name;
// 替换appServiceName为项目工程名
targetFile = targetFile.replaceAll("appServiceName", appServcieName);
// 替换文件内容并写到文件当中
merge(lines, targetFile, appServcieName);
}
}
}
System.out.println("done!");
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void merge(List<String> contents, String targetFile, String appServcieName)
{
try
{
// 文件的内容合并到StringBuffer当中
StringBuffer sb = new StringBuffer();
for (String content : contents) {
sb.append(content).append("\n");
}
// 通过velocity模板引擎替换文件内容的占位符
VelocityEngine ve = new VelocityEngine();
ve.init();
VelocityContext context = new VelocityContext();
context.put("appServiceName", appServcieName);
StringWriter writer = new StringWriter();
// 模板引擎替换占位符,并将内容写入文件当中
ve.evaluate(context, writer, "", sb.toString());
FileUtils.writeStringToFile(new File(targetFile), writer.toString(), "UTF-8");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
说明:
- 源码实现中读取service-eve.jar并开始遍历模板工程/template目录
- 第一步替换文件路径当中的appServiceName为新建项目的项目名
- 第二步替换java源码文件中的占位符为新建项目的项目名
- 核心思路在于读取jar包内容替换目录和内容,内容需要通过velocity引擎进行文本替换
结语
论java我很服海军,上述实现也是海军的杰作,让我也开了眼界