编写代码生成器的一些问题与思考
1.引言
去年7月开始参加工作,刚开始被先后分配了两个制作基础页面的任务,是常规的增删改查,包括前端页面的vue文件以及后端实体类和各逻辑层的接口、实现类,总共需要创建9个文件,1个vue、7个java,1个xml。
虽然可以使用MybatisGenerator根据数据库表自动生成实体类和Mapper层文件,但再往上的逻辑层就需要手动编写。Ctrl+C,Ctrl+V,再根据具体的实体类名称去修改,前前后后花了1个小时把一个基础页面调通。觉得实在太麻烦了,所以产生了写个代码生成器的念头,一键生成9个文件,从而告别复制粘贴。效果如下所示:
2.技术点
- 根据代码模板生成,使用了常用的FreeMarker作为模板引擎。
- 为了简化使用操作,做了个GUI把已有的MybatisGenerator和新编写的生成代码逻辑封装起来。使用了JavaFX,可以拖控件,挺省事的。
- 为了保存生成文件的目录信息、连接数据库信息,以及在不同项目中切换,使用了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将不会生效;但如果是基于接口动态代理,则不会因为失误出现这个问题。
最后,尝试给这个问题一个暂时的答案:
在大型团队进行面向企业应用开发中,为了避免意外的风险,尽量使用接口编程。
而当团队规模不大,了解不使用接口的风险,能够承受该风险,业务逻辑变更频繁时,不是面向企业应用开发,可考虑不使用接口,直接通过实现类开发。
- LINQ驱动数据的查询功能
- JS魔法堂:jQuery.Deferred(jQuery1.5-2.1)源码剖析
- 一步一步学习Bootstrap系列--表单布局
- Java进阶01 String类
- linux(十二)之用户管理
- Dapper.Net 应用
- linux(十)配置ssh免密登录实现
- 微信群也有群相册功能了,终于可以给手机相册腾出空间了!
- PHP5.3、PHP5.4安装ZendOptimizer
- linux(十一)之初始化文件
- C编译: 动态连接库 (.so文件)
- 腾讯叮当首都发布生态合作伙伴计划,全面开放AI能力
- Python火爆的背后的应用领域是数据挖掘、大数据和人工智能的应用吗?
- linux(九)之网络基础
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 使用 Eureka 简单实现服务健康监控日志分析
- Flask 入门系列教程(一)
- Flask 入门系列教程(二)
- CVE-2020-1472
- 盘点提高国内访问 GitHub 的速度的 9 种方案
- 如何用 Serverless 优雅地实现图片艺术化应用
- Unity3D网络通讯(四)--Socket通讯之Tcp通讯
- 笔记|Unity异步处理与UI Text显示的问题
- 项目中的全局缓存导致了内存泄露?
- 架构设计 | 分布式体系下,服务分层监控策略
- Hadoop框架:单服务下伪分布式集群搭建
- 架构设计 | 基于Seata中间件,微服务模式下事务管理
- 编程体系结构(03):Java集合容器
- SpringBoot2 集成日志,复杂业务下的自定义实现
- 事件统计 | performance_schema全方位介绍