原创 CrimsonHu 2024-12-16 08:30 重庆
点击关注公众号,“技术干货” 及时达!
项目介绍
这是我在今年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.KotlinCompilegroup = "com.hu.cablecar"version = "0.0.1"description = "缆车"// Java 版本java {sourceCompatibility = JavaVersion.VERSION_21targetCompatibility = JavaVersion.VERSION_21}// 用于支持 Java 与 kotlin 混写,以及 lombok 依赖引入plugins {javakotlin("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自己的代码,不会引入它的依赖fastjson2implementation(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任务:打包为jarenabled = truemanifest {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 内核,用处就是我在里面放了我的个人网站:
日语笔记 - intelyes.club
总结
再回顾下项目结构:
我们只用关心根目录下的配置文件以及每个子项目下的配置文件:
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 非常多,代码就暂时不分享了。等到以后整理完成并完善后,会考虑单独写一篇文详细介绍其功能。
如有不懂的欢迎留言讨论!
点击关注公众号,“技术干货” 及时达!
