作者生涯
距离上次写文还是在上次(大半年以前了)。因为年初在准备研究生复试,现在研一上学期快结束了,今年一直没多少时间写写文章分享。
我研究生读的是日语语言文学这一专业。因为我本质上来说不喜欢理工科的知识,本科专业是机械自动化,其实那会四年下来啥都没学到(主要是数学太菜了,高数学不懂,导致很多专业课学的一知半解。唯一成就就是拿下了英语六级)~~
后来自学 Java ,干了几年全栈,到了那时候人在武汉(你们懂的),于是就在家里了。那会也没啥很多的工作,就再次开启了自学之路,体内的文科人血脉觉醒,从零基础一路自学干到日专学硕。
开个成分复杂的玩笑:“不会做后端开发的文科研究生不是一名掌握前端开发的合格机械工程师。”
项目介绍
这是我在今年9月研一开学后,选定了研究方向,就顺手用 Java 撸了一个日语语言分析工具。
除了它本来的功能,它在技术上的特点是如下三条:
- 在 springboot 中运行 java swing 界面(下图的界面就是用 swing 实现的)
- 使用 gradle 搭建的父子项目,并支持 kotlin 混写
- 内置一个 chromium 内核
本文就着重介绍第二点。
主要技术:java + kotlin + swing + springboot + gradle
运行环境:JetBrains Runtime with JCEF(Java 21)
在我看来,每一次项目开发都应该是一次技术的积累,经验的进步。 所以在这次的项目搭建中,就尝试了用 gradle 去代替以前 maven 父子项目的方案,同时拥抱下 kotlin 。为什么会写这篇文章呢?因为在这次项目搭建过程中踩了很多坑。在这个项目中收获最大的就是掌握了基于 gradle.kts 的项目搭建。
除此之外,界面是 swing 做的,这也是我的一个强项,用 Java 手搓 GUI 。
以下是主要界面:
项目结构
我不喜欢分享很难懂的东西。我尽力将本文内容写得简单,让大家都能看懂,也能够真正有所收获。
直接来看项目结构:
如图,项目结构其实很简单。虽然文件夹很多,但是真正只用关心的是中间的 gradle 子项目模块
、 build.gradle.kts
、 settings.gradle.kts
这三个地方。
什么?你还不会创建 gradle 项目?其实很简单, Spring Initializr 会帮你创建。如图:
创建项目完成后,就可以将其改造为父子项目了。
父模块配置
父模块配置依靠项目根目录下的 build.gradle.kts
、 settings.gradle.kts
。
先看 settings.gradle.kts
:
rootProject.name = "cable-car"
include(":1-common")
include(":1-reactivex")
include(":1-spring-application")
include(":2-jp-analysis-core")
include(":3-client-framework")
include(":4-web-jp-learning")
可以很明显地看到,该配置文件中的 include(":xxxxx")
就是将上述项目结构中的各个模块引入到 settings.gradle.kts
中(记得不要忘记冒号)。 rootProject.name
即为项目名。
再来看 build.gradle.kts
:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
group = "com.hu.cablecar"
version = "0.0.1"
description = "缆车"
// Java 版本
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
// 用于支持 Java 与 kotlin 混写,以及 lombok 依赖引入
plugins {
java
kotlin("jvm") version "1.9.24"
kotlin("plugin.lombok") version "1.9.24"
id("io.freefair.lombok") version "8.10"
}
// 全局配置
allprojects {
// 将上述 plugins 应用到子模块
plugins.apply(JavaPlugin::class.java)
plugins.apply("kotlin")
plugins.apply("kotlin-lombok")
plugins.apply("io.freefair.lombok")
// 仓库地址
repositories {
maven {
url = uri("https://maven.aliyun.com/nexus/content/groups/public/")
}
mavenCentral()
mavenLocal()
}
tasks.withType<JavaCompile>() {
options.encoding = "UTF-8"
}
tasks.withType<Javadoc>() {
options.encoding = "UTF-8"
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "21"
}
}
}
这就是 build.gradle.kts
需要准备的全部内容,可以很直观地看到,它主要就做了三件事:
- 定义了
Java
版本 - 引入了
Java
、kotlin
、lombok
相关支持,并应用到子项目 - 配置了依赖的
maven
下载地址
子模块
接下来就是子模块的介绍,怎么创建子模块,怎么将一个子模块引入到另一个子模块。
1-common:
在上文项目结构中,我们先看最简单的子模块 1-common
:
这个模块只包含三个 Java
写的工具类,以及一个 build.gradle.kts
配置文件。来看看配置文件的内容:
description = "公共基础模块"
dependencies {
implementation("com.alibaba.fastjson2:fastjson2:2.0.45")
}
配置文件内容很简单:
description
描述了这个模块的名字- 仅在
dependencies
中引入了fastjson2
依赖
现在就能看到为什么要使用 gradle 来搭建项目了。因为相比于 maven , gradle 的配置文件真的很简单。
1-reactivex:
看完最简单的子模块 1-common
后,来看看稍微复杂一点点的 1-reactivex
模块:
这个模块的作用是用 rxJava 实现了一个发布订阅的功能。这里的代码就是一直没时间单独写篇文章的那个“跨组件通信的 Java 版本”(没时间,真没时间...《下次一定》)。我只用RxJS,却搞定了三大框架的跨组件通信,甚至还能适用于Java(一、Angular篇)跨组件通信,远比你想象的要简 - 掘金
Java swing 的跨组件通信我就是用这个方法去做的(所以说功能的实现思路都是相通的。全栈的目的,就在于不要将自己局限在一个语言甚至一个框架里面)。
好了,题外话说完,来聊回这个模块的配置。来看 build.gradle.kts
配置文件:
description = "RX"
dependencies {
// 像这样的写法,只会引入1-common自己的代码,不会引入它的依赖fastjson2
implementation(project(":1-common"))
implementation("io.reactivex.rxjava3:rxjava:3.1.9")
}
从 dependencies
可以看到,这个模块不仅引入了 rxjava
,也引入了子模块 1-common
,因为该模块用到了它里面工具类的一个方法。
2-jp-analysis-core:
同样的,这也是一个工具模块,用来做日语文字解析。代码结构与配置文件如下:
build.gradle.kts
:
description = "日语分析核心模块"
plugins {
}
dependencies {
implementation(project(":1-common"))
implementation("commons-io:commons-io:2.15.1")
}
可以看到,这个模块引入了 commons-io
和子模块 1-common
。
3-client-framework:
重点来了,这是项目启动的 main
方法所在的模块,也是整个项目的界面。
它很复杂。引入了其它各个子模块,在使用 springboot
启动 swing
界面的同时,也使用了 Java
与 kotlin
混写。
来看看它的配置文件 build.gradle.kts
:
description = "客户端UI框架"
plugins {
// spring相关配置
kotlin("plugin.spring") version "1.9.24"
id("org.springframework.boot") version "3.3.4"
id("io.spring.dependency-management") version "1.1.6"
}
dependencies {
// 引入各个子模块
implementation(project(":1-common"))
implementation(project(":1-reactivex"))
implementation(project(":1-spring-application"))
implementation(project(":2-jp-analysis-core"))
implementation(project(":4-web-jp-learning"))
// 引入maven依赖
// 在上面引入子模块时,不会引入子模块自己的依赖
// 所以如果在此使用了同样的依赖例如这里的commons-io与rxjava,需要在该模块中再次引入
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-tomcat")
implementation("io.reactivex.rxjava3:rxjava")
implementation("commons-io:commons-io:2.15.1")
// 引入本地jar依赖
implementation(files("lib/ui/flatlaf-3.5.2.jar"))
implementation(files("lib/ui/flatlaf-intellij-themes-3.5.2.jar"))
implementation(files("lib/ui/JTattoo-1.6.13.jar"))
}
tasks.jar {
// 设置gradle任务:打包为jar
enabled = true
manifest {
attributes(mapOf("Main-Class" to "com.hu.cablecar.client.framework.ClientFrameworkApplication"))
}
}
可以在配置文件中看到:
plugins
做了spring
相关配置,以及spring
的kotlin
配置dependencies
中引入各个子模块以及相关依赖tasks.jar
配置了gradle
的打包为jar
的任务
配置了 task.jar
后,可以在此执行相应的任务,将该模块打包为 jar 。
于是能够看出,即使是复杂的模块,其 gradle
配置依然简单清爽。如果是 maven
的 pom.xml
配置,那么肯定就不只这么一点代码了。
由于在根目录下的 build.gradle.kts
中配置了 kotlin
,以及在该模块也进行了 spring
的 kotlin
配置,在该模块中,是可以直接写 kotlin
代码的,并能和 java
代码互相引用。
该图是之前写过的这个文章的内容的代码,在此将其改为 kotlin
了:一种解决Swing中JLabel图片在高分屏上显示不正常的方案Java Swing 在高分屏上图片显示模糊,即 JLab - 掘金
该图是用 kotlin
去写了一个 spring
配置类。
开头有讲到其内置了一个 chromium
内核,用处就是我在里面放了我的个人网站:
总结
再回顾下项目结构:
我们只用关心根目录下的配置文件以及每个子项目下的配置文件:
1-common
1-reactivex
1-spring-application
2-jp-analysis-core
3-client-framework
4-web-jp-learning
在 gradle
父子项目中,只用关心根目录下的 build.gradle.kts
、 settings.gradle.kts
,以及各个子模块的 build.gradle.kts
。这一点和 maven
父子项目相似。
但是配置文件的内容很比 maven
的 pom.xml
简单:
- 根目录下的
settings.gradle.kts
中声明每个子模块 - 根目录下的
build.gradle.kts
中进行java
、kotlin
相关配置等 - 各个子模块下的
build.gradle.kts
中声明该模块的依赖引用等
这个项目由于还没提交到 git ,本地依赖与 native 非常多,代码就暂时不分享了。等到以后整理完成并完善后,会考虑单独写一篇文详细介绍其功能。
如有不懂的欢迎留言讨论!