效果展示—— 若发现文章有任何不正确的地方,敬请指出,望不吝赐教,谢谢!
文章目录
效果展示一、准备知识
相关工具:Scene Builder [点击下载](https://openjfx.cn/scene-builder/) 二、运行环境三、引入依赖四、项目结构介绍五、配置数据库交互层DAO层
5.1 连接数据库配置5.2 MyBatis配置5.3 编写MyBatis工具类 DBUtils.java5.4 DAO层 六、编写Service业务逻辑层七、JavaFX相关的类
7.1 启动类7.2 编写StageController窗口控制类7.3 公用接口ControlledStage7.4 组件的Controller类
7.4.1 登录控制层7.4.2 主页面控制层 八、核心业务逻辑
在登录窗口进行登录,登录验证成功后跳转到另一个窗口,若失败则弹出提示框
JavaFX 中文官网:点击查看
javaFX 是一个开源的下一代客户端应用平台,适用于基于Java构建的桌面、移动端和嵌入式系统。 它是许多个人和公司的共同努力的成果,目的是为开发丰富的客户端应用提供一个现代、高效、功能齐全的工具包。
JavaFX架构:
Scene Builder 是一款创建美丽的用户界面,并将您的设计变成一个交互式原型。Scene Builder通过创建可直接用于JavaFX应用程序的用户界面,缩小了设计师和开发人员之间的差距。
JavaFX相关的资料网站:
二、运行环境JavaFXChina 是国内JavaFX资料最为系统全面的网站之一,他们靠业余时间维护此站点,希望能帮到大家。
Windows10MySQL8JDK8Maven3.8.4IDEA 2021.3.2 Ultimate其他的版本号请查看引入的Maven依赖 三、引入依赖
pom.xml
四、项目结构介绍4.0.0 com.uni cs-plugins 1.0-SNAPSHOT cs-plugins UTF-8 5.8.1 8 org.openjfx javafx-controls ${java.version} org.openjfx javafx-fxml ${java.version} org.kordamp.bootstrapfx bootstrapfx-core 0.4.0 org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test mysql mysql-connector-java 8.0.25 log4j log4j 1.2.17 org.apache.maven.plugins maven-compiler-plugin 3.8.1 ${java.version} ${java.version} org.openjfx javafx-maven-plugin 0.0.8 default-cli com.uni/com.uni.App app app app true true true
由于工具会涉及数据库的相关交互,同时有视图的概念,这里采用类似于Web的MVC模式进行设计,其中的M表示Model模型,在这里对应于JavaFX的Stage窗口,V则表示View视图,对应于JavaFX的Scene,C表示Controller控制层,JavaFX官方API有提供getController()的方法。
其他的相关介绍:
| 名称 | 描述 |
|---|---|
| controller | 存储控制层相关的类,其中比较重要的是Stage相关的类 |
| dao | 存储负责数据库交互的Mapper接口,实现则通过resources文件夹里的XML映射文件 |
| pojo | 存储实体类 |
| service | 存储业务层接口以及对应的Impl实现类 |
| utils | 存储一些工具类,比如获取MyBatis负责数据库交互的mapper类 |
| App.java | 继承于JavaFX的Application类,主要用于启动JavaFX程序以及配置相关的窗口信息 |
| resources/com/uni/mapper/ | 存储MyBatis里Mapper接口对应的XML映射文件 |
| login.fxml | 使用SceneBuilder工具进行可视化设计的登录的页面 |
| main.fxml | 使用SceneBuilder工具设计的登录成功后的主页面(这里涉及页面切换概念) |
| lib | 存储一些关键的jar包,可以忽略,Maven导入依赖就行 |
| database.properties | 配置JDBC连接的一些信息,数据库连接地址、驱动类、用户、密码 |
| mybatis-config.xml | 配置项目的MyBatis |
5.1 连接数据库配置
/resources/database.properties
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/数据库名称?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull user=数据库用户名 password=数据库密码
这里需要注意,为方便代码里获取到配置内容,最好是将该其放在resources资源文件夹里,方便查找以及访问
JDK官方有API操作properties的配置文件,所以不需要手动进行IO流的转换,这里则通过MyBatis框架提供的API直接连接数据库
范例: 根据MySQL的配置文件创建MyBatis框架的工厂(可通过工厂取Mapper类、执行方法等)
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.session.SqlSessionFactory; String CONFIG_PATH = "mybatis-config.xml"; InputStream config = Resources.getResourceAsStream(CONFIG_PATH); factory = new SqlSessionFactoryBuilder().build(config);5.2 MyBatis配置
在没有SpringBoot下配置MyBatis果然有些复杂了,不过稍微总结一下,也不难理解,它的主要配置内容如下:
引入相关的数据库配置文件(可以不引入直接写值,但这会导致耦合度提升,不利于代码修改)特殊的设置(这里可有可无,而且配置类型比较多,笔者就只改了个日志输出的类logImpl,这样就可以按照自己的日志配置来设置别名,一是简化实体类(这样可以省略包名看起来简短)二是简化Mapper类的接口类,这样在写XML时指定namespace就可以省略Mapper接口的包名)配置数据库环境最后,设置Mapper的XML映射文件地址,这样MyBatis才能找到对应的XML映射
/resources/mybatis-config.xml
5.3 编写MyBatis工具类 DBUtils.java
关于MyBatis的使用,之前浅学了SSM+SpringBoot,就只记住了@Repository、@Autowired这两个注解,当不使用注解的时候,有多种选择进行创建,这里则使用其中的工厂模式来创建。
DBUtils.java
package com.uni.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class DBUtils{
private static final String CONFIG_PATH = "mybatis-config.xml";
private static SqlSessionFactory factory;
// 单例模式
static {
try {
// 获取配置文件输入流
InputStream config = Resources.getResourceAsStream(CONFIG_PATH);
// 创建工厂
factory = new SqlSessionFactoryBuilder().build(config);
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取session会话
public static SqlSession getSession(){
return factory.openSession();
}
// 关闭session会话
public static void close(){
//..
}
}
5.4 DAO层
在编写DAO层接口前,需先编写相应的实体类,比如笔者需要实现登录效果,这里就设计一个用户User的Pojo(简单的Java对象) 类
User.java
package com.uni.pojo;
public class User {
private Integer id;
private String username;
private String password;
public User(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
", password='" + password + ''' +
'}';
}
}
Dao层接口类:UserMapper.java
package com.uni.dao;
import com.uni.pojo.User;
public interface UserMapper {
User selectByPassword(User user);
}
Dao层XML映射文件:UserMapper.xml
六、编写Service业务逻辑层
接口类:UserService.java (实现类放在最后的部分,因为它用到了JavaFX的相关的一些类)
package com.uni.service;
import com.uni.pojo.User;
public interface UserService {
public User login(User user);
}
七、JavaFX相关的类
7.1 启动类
App.java
package com.uni;
import com.uni.controller.StageController;
import javafx.application.Application;
import javafx.stage.Stage;
public class App extends Application {
public static String mainViewID = "MainView";
public static String mainViewRes = "main.fxml";
public static String loginViewID = "LoginView";
public static String loginViewRes = "login.fxml";
@Override
public void start(Stage primaryStage) {
//新建一个StageController控制器
StageController stageController = new StageController();
//设置默认的主窗口
//加载两个窗口
stageController.loadStage(loginViewID, loginViewRes);
stageController.loadStage(mainViewID, mainViewRes);
//切换主窗口
stageController.change(loginViewID);
}
public static void main(String[] args) {
launch();
}
}
7.2 编写StageController窗口控制类
该类使用HashMap结构来存储对应的窗口名称、对应的Stage窗口对象,使用双端队列进行窗口的切换控制,目前还存在一些不足,仅作参考,相关的方法以及介绍。
| 方法名 | 描述 |
|---|---|
| void addStage(String a, Stage b) | 根据名称添加Stage窗口到Map结构中 |
| Stage getStage(String a ) | 根据Stage窗口名称获取对应的Stage对象 |
| change(String a) | 根据Stage名称切换到对应的窗口(要求Stage存在于Map中)设计双端队列的处理 |
| loadStage(String a, String b, StageStyle …) | 根据Stage的名称和对应的fxml文件位置,加上可选的窗口风格加载新的Stage对象,加载完毕后会保存到Map结构 |
| unloadStage(String a) | 根据Stage名称删除相应的窗口 |
| stages | 静态变量,存储所有的Stage名称以及Stage信息 |
| queue | 静态变量, 存储当前的窗口序列,可用于返回操作(这里暂未使用) |
StageController.java
package com.uni.controller;
import com.uni.App;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.Deque;
import java.util.HashMap;
import java.util.linkedList;
public class StageController {
private static HashMap stages = new HashMap(); // 存储所有Stage
private static Deque queue = new linkedList<>(); // 窗口双端队列(先进后出)(切换窗口有效)
public void addStage(String name, Stage stage) {
stages.put(name, stage);
}
public Stage getStage(String name) {
return stages.get(name);
}
public void change(String stageName) {
// 首先获取当前的state
Stage stage = stages.get(stageName);
if(stage == null)
throw new NullPointerException("未获取到指定的Stage,请确保它已经使用过addStage()/loadStage()");
else {
// 遍历队列
if (queue != null && !queue.isEmpty()) {
// 将队首的传移动到队尾
queue.push(queue.pollFirst());
// 关闭所有窗口
for (Stage temp : queue) {
// 若当前窗口是要切换的窗口,则移除(防止重复)
if(temp.equals(stage))
queue.remove(temp);
temp.close();
}
}
// 头插法
queue.addFirst(stage);
// 启动队首窗口
queue.getFirst().show();
}
}
public boolean loadStage(String name, String resources, StageStyle... styles) {
try {
//加载FXML资源文件
FXMLLoader loader = new FXMLLoader(App.class.getResource(resources));
Pane tempPane = loader.load();
//通过Loader获取FXML对应的ViewCtr,并将本StageController注入到ViewCtr中
// 通过官方API获取当前Stage的Controller,利用官方提供的泛型设置将其返回为接口ControlledStage
ControlledStage controlledStage = loader.getController();
// 调用相应的set方法,为当前Staged设置现在的Controller列
controlledStage.setStageController(this);
//构造对应的Stage
Scene tempScene = new Scene(tempPane);
Stage tempStage = new Stage();
tempStage.setScene(tempScene);
//初始化窗口的风格
for (StageStyle style : styles) {
tempStage.initStyle(style);
}
//将设置好Stage添加到Map结构
this.addStage(name, tempStage);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean unloadStage(String name) {
if (stages.remove(name) == null) {
System.out.println("窗口不存在,请检查名称");
return false;
} else {
System.out.println("窗口移除成功");
return true;
}
}
}
7.3 公用接口ControlledStage
该接口的作用是要求实现setStageController()方法,StageController则是上面那个自定义的用于处理JavaFX窗口显示的控制层类,当我们绑定一些按钮,要实现界面跳转时就得通过Controller层的逻辑代码进行处理。所以我们规定其他的Controller类都需要实现这个接口,这提示该类需要有一个StageController对象作为成员变量,方便进行窗口切换的操作。
package com.uni.controller;
public interface ControlledStage {
public void setStageController(StageController stageController);
}
7.4 组件的Controller类
7.4.1 登录控制层
相关的fxml,通过SceneBuilder可视化工具进行设计
在登录控制层中,通过@FXML获取到了fxml格式的UI配置文件里使用fx:id标记的组件,然后直接调用业务层的login方法进行登录,最后根据返回的结果是否为null则判断是否登录成功,若登录成功则需要跳转窗口,若失败则弹出提示窗口(这个功能在业务逻辑层实现)
package com.uni.controller;
import com.uni.App;
import com.uni.pojo.User;
import com.uni.service.UserService;
import com.uni.utils.ServiceUtils;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import java.net.URL;
import java.util.ResourceBundle;
public class LoginController implements ControlledStage, Initializable {
private static UserService userService = ServiceUtils.userService;
StageController controller;
@FXML
private TextField username;
@FXML
private PasswordField password;
@FXML
private void btLogin(){
User login = userService.login(new User(username.getText(), password.getText()));
if(login != null)
goToMain();
}
public void setStageController(StageController stageController) {
this.controller = stageController;
}
public void initialize(URL location, ResourceBundle resources) {
}
public void goToMain(){
controller.change(App.mainViewID);
}
}
7.4.2 主页面控制层
这里还没有设计逻辑跳转,但可以参照之前的登录控制层进行添加
package com.uni.controller;
public class MainController implements ControlledStage{
private StageController controller;
@Override
public void setStageController(StageController stageController) {
this.controller = stageController;
}
}
八、核心业务逻辑
之前声明了UserService接口,定义了login()方法,先进行补充:
这里用到了JavaFX的alert警告框,通过调用showAndWati()方法就可以弹出这个窗口,则密码验证失败后弹出提示用户输入错误,方便用户操作。
package com.uni.service.impl;
import com.uni.dao.UserMapper;
import com.uni.pojo.User;
import com.uni.service.UserService;
import com.uni.utils.DBUtils;
import javafx.scene.control.alert;
public class UserServiceImpl implements UserService {
private static UserMapper userMapper;
public UserServiceImpl(){
if(userMapper == null)
userMapper = DBUtils.getSession().getMapper(UserMapper.class);
}
@Override
public User login(User user) {
User result = userMapper.selectByPassword(user) ;
if(result == null){
alert alert = new alert(alert.alertType.WARNING);
alert.setTitle("登录提示");
alert.setContentText("输入的账号或密码有误");
alert.showAndWait();
}
return result;
}
}



