编写代码生成器的一些问题与思考

时间:2022-07-22
本文章向大家介绍编写代码生成器的一些问题与思考,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1.引言

去年7月开始参加工作,刚开始被先后分配了两个制作基础页面的任务,是常规的增删改查,包括前端页面的vue文件以及后端实体类和各逻辑层的接口、实现类,总共需要创建9个文件,1个vue、7个java,1个xml。

虽然可以使用MybatisGenerator根据数据库表自动生成实体类和Mapper层文件,但再往上的逻辑层就需要手动编写。Ctrl+C,Ctrl+V,再根据具体的实体类名称去修改,前前后后花了1个小时把一个基础页面调通。觉得实在太麻烦了,所以产生了写个代码生成器的念头,一键生成9个文件,从而告别复制粘贴。效果如下所示:

2.技术点

  1. 根据代码模板生成,使用了常用的FreeMarker作为模板引擎。
  2. 为了简化使用操作,做了个GUI把已有的MybatisGenerator和新编写的生成代码逻辑封装起来。使用了JavaFX,可以拖控件,挺省事的。
  3. 为了保存生成文件的目录信息、连接数据库信息,以及在不同项目中切换,使用了SQLite

3.问题

以下记录所遇到的一些问题:

1. 在MybatisGenerator生成实体类(java文件)后,需要将实体类编译成class文件,再加载进JVM中,才能通过反射读取到实体类属性。实现步骤如下:

  • 读取java文件内容
  • JavaCompiler动态编译生成class文件
  • 通过反射利用ClassLoader的addURL方法,将class文件的路径加入到classpath中
  • 加载类(Class.forName)

对第三步做下解释,需要加入classpath的主要原因是最后的程序被打包成了jar包,而运行时生成的class文件是不会在jar包中,那么其路径也不在classpath中。

2. 在用IDE开发时,可以通过File类成功找到资源文件:

File configFile = new File("generatorConfig.xml");

但是打包成jar包后,就连资源文件一起打包了,而这时是无法通过file:/e:/codeGenerator.jar/generatorConfig.xml这种形式的文件URL找到文件的。

这是因为jar包是个单独文件,而不是文件夹,而需要通过ClassLoader提供的getResourceAsStream方法获取jar包中的内容。

InputStream configFile = Thread.currentThread().getContextClassLoader().getResourceAsStream("generatorConfig.xml");

4.思考

《阿里巴巴Java开发手册》中关于应用分层推荐使用4层,分别是Web层、Service层、Manager层和DAO层。而我们在开发的过程中经常能看到每一层都需要面向接口编程,例如xxxManager。这么做是为了达到“对修改封闭,对扩展开放”的原则。

但实际情况是一个接口通常只有一个实现类,例如xxxManagerImpl,大多数的代码通常都并不会有扩展的情况。这样会导致的问题就有像前文所说一个简单基础数据的增删改查就需要7个java类,代码量增加了。难免有些是在为了使用接口而使用接口的味道。

那么是否需要面向接口编程

下面让我们试想一下,在一个接口只有一个实现类的情况下,不使用面向接口编程的好处和坏处。

好处,很显然,我们只需要编写、维护一份代码(实现类),代码量减少了。

坏处,则是不可控的类信息暴露控制。例如,想通过AOP实现方法级别的缓存,并且使用注解标记方法,如果我们失误将注解标记在了private方法上,而由于基于CGLIB的AOP是通过生成子类来创建代理,所以该AOP将不会生效;但如果是基于接口动态代理,则不会因为失误出现这个问题。

最后,尝试给这个问题一个暂时的答案

在大型团队进行面向企业应用开发中,为了避免意外的风险,尽量使用接口编程。

而当团队规模不大,了解不使用接口的风险,能够承受该风险,业务逻辑变更频繁时,不是面向企业应用开发,可考虑不使用接口,直接通过实现类开发。