JDK21 已于2023年9月发布,其新特性中,「虚拟线程」备受瞩目自不必说,后或将改写并发编程现有之格局,目前观望中,除此,引入了新的内存 api 以访问 JVM 以外的内存地址,以前则是需要使用 JNI 技术调 c/c++ 函数来实现,这为 Java 写歪瓜提供了新的可能,JVM 或将纳入检测行列。前有放弃指针,后有操作内存,前有一处编译到处运行,后有 GraalVM 构建原生镜像,颇有「前人栽树,后人砍树」之意,暂且不论。其余,择 Record 记录类,字符串模板,或可于项目中小试一番,以观其效。
Record 特性
传统类型实体 VS Record 类型实体
于当下,一个传统的实体类(JavaBean),通常拥有如下定义,默认的无参构造、属性私有、通过 getter、setter 访问其私有属性、序列化(可选)、重写 toString、equals(可选)。演示如下:
// 借助 lombok 生成 getter/setter @Data public class User { private Integer id; private String name; private Integer age; private String gender; private String phone; }
record 类似 enum,只是语法糖层面的新东西,对类做了隐式处理,反编译可知,即:为该类生成一个全参数构造,以及相应的 getter 方法,没有 setter 方法,成员属性只能定义在()中,且赋值只能通过构造赋值,其 getter 亦不同于传统类型 getXxx 的命名方式,字段名即方法名。演示如下:
public record User(Integer id, String name, Integer age, String gender, String phone) {}
使用普通构造注入 VS 使用 Record 类型进行依赖注入
Spring 注入方式有字段注入、setter 注入、构造注入三种。因构造注入方式更易把控,且不可变性,故而更受官方推崇。偷懒,省去其中 service 部分,使用构造生成 controller 演示代码如下:
@RestController @RequiredArgsConstructor public class UserController { // 构造注入,借助 lombok 生成必要的参数构造 private final UserDao userDao; ...... }
使用 Record 生成 controller 并进行依赖注入,亦属于构造注入,演示如下
@RestController public record UserController(UserDao userDao) {...... }
注:如果明确知道一个框架底层使用了反射获取无参构造以创建对象,或通过 get+字段名取值、set+字段名赋值方式时,就不要用 Record了,盲猜一波,或许某些涉及 JSON 转化的框架中可能会存在这样的代码,此时不要用 Record,Record 目前局限性还是挺大的,用处是真不多,也不能取代 lombok。
字符串模板特性
删繁就简(“三……三秋树”——来自画外音,语气弱弱),引入了符号""",帮我们省去繁杂的字符串拼接过程。
MyBatis,我第一个想到的受益者就是它。原先使用注解方式生成 mapper,因其使用动态 sql 时存在大量标签,需要在注解上大量拼接字符串方可完成,且可读性极差,费力不讨好,故该方式没有流行开来,今有字符串模板补其缺漏,岂非妙哉?又闻「(天下 SQL)分久必合,合久必分」,亦合此理!sql 语句从方法体中移至 xml,又移至方法签名上,方便阅读亦不失灵活,该代理依旧代理,该解耦依旧解耦,无 xml 之乱花迷眼,无跳入跳出之劳吾形、乏吾身,不可谓不称心也。
或言:“何必理会?吾有一法,曰「MyBatis plus」,一把梭哈之,岂非更妙?”,对曰:“岂不闻,大道至简,万法归宗,及君悟,必歌之以「当~~~」”,其实,这种三方辅助框架,用多了,方不方便,也就那样,另外,我为何要写sql?因为我享受掌控全局的快感 因为我对这SQL,爱得深沉 因为我傻呗。我也是好久没写 sql 了,感觉偶尔写一写 sql 也是蛮不错的。(来自bed言bed语)字符串模板结合 MyBatis,演示代码如下:
public interface UserDao { // as 别名映射 @Select(""" SELECT u_id AS id, u_name AS name, u_age AS age, u_gender AS gender, u_phone AS phone FROM t_user """) ListfindAll1(); // 字段一一映射 -- 适用于常规类型实体 @Results(id = "user", value = { @Result(column = "u_id", property = "id", id = true), @Result(column = "u_age", property = "age"), @Result(column = "u_gender", property = "gender"), @Result(column = "u_phone", property = "phone") }) @Select("select * from t_user") List findAll2(); // 构造器映射 -- 适用于 Record 类型实体 @ConstructorArgs({ @Arg(column = "u_id", javaType = Integer.class), @Arg(column = "u_name", javaType = String.class), @Arg(column = "u_age", javaType = Integer.class), @Arg(column = "u_gender", javaType = String.class), @Arg(column = "u_phone", javaType = String.class) }) // 内含 where/if 等动态标签的,需用 """) List findAll3(@Param("user") User user); }
后记:
遗憾的是,于 """中的代码提示功能尚无,有心人可自己开发一款 idea 插件,另外,不得不说,依旧又臭又长,或可改用更简洁的语义以完成sql拼接。
由于 xml 配置方式属实是冗长,spring 也早早就放弃了,JavaConfig 明显更人性化、现代化,其余配置也是改用了更精简的 properties 或 yaml。
关于如何让 MyBatis 变得更精简,我倒是有一个不成熟的想法:搞一个 MyBatis Minus 框架,功能上 + +,代码却 - -,直接摒弃其解析 xml 相关代码,直接注解加字符串模板,定义一套全新的语义、词法。当初试想过搞这么个框架,不过没那么多空闲时间,提供思路,留给有心人去搞吧。
下附相关框架:
语法词法分析框架:Antlr4、Javacc、jflex
字符串模板引擎框架:Enjoy、Thymeleaf,FreeMarker,Velocity
追责声名:
文中以上写法,均为测试案例,任何人因使用采用而遭致意外乃至损失的,由「三国杀官方」以及「动视暴雪」全权负责。