1. 架构设计与集成模式
1.1 Spring Boot 与 JavaFX 的分层架构设计
Spring Boot 与 JavaFX 的整合需要精心设计的分层架构,以充分利用两个框架的优势。
标准分层架构
┌───────────────────────────────────────────────────┐
│ 表现层 (View Layer) │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ FXML 文件 │ │ JavaFX 控件 │ │
│ └─────────────┘ └─────────────┘ │
└───────────────────────────────────────────────────┘
▲
│
┌───────────────────────┼───────────────────────────┐
│ 控制层 (Controller) │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ JavaFX 控制器 (FXML Controller)│ │
│ │ 由Spring管理依赖注入 │ │
│ └─────────────────────────────────────┘ │
└───────────────────────────────────────────────────┘
▲
│
┌───────────────────────┼───────────────────────────┐
│ 服务层 (Service Layer) │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │Spring Services│ │Spring Components│ │
│ └─────────────┘ └─────────────┘ │
└───────────────────────────────────────────────────┘
▲
│
┌───────────────────────┼───────────────────────────┐
│ 数据层 (Data Layer) │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │Spring Data │ │Repositories │ │
│ │Repositories │ │ │ │
│ └─────────────┘ └─────────────┘ │
└───────────────────────────────────────────────────┘
职责分离原则
- UI 层(JavaFX):负责用户界面的渲染和用户输入处理
- 控制器层(JavaFX Controller + Spring):处理用户交互,调用服务层
- 服务层(Spring Service):包含业务逻辑,处理事务
- 数据访问层(Spring Data):处理数据持久化
整合策略要点
- UI与业务逻辑分离:JavaFX负责界面,Spring负责业务逻辑
- 控制器作为桥梁:JavaFX控制器由Spring管理,连接UI和服务
- 模型数据共享:使用DTO或专用模型对象在层间传递数据
- 依赖注入贯穿:所有组件使用Spring的依赖注入机制
示例项目结构:
src/main/java
├── com.example.app
│ ├── SpringBootJavaFxApplication.java // 应用入口
│ ├── config
│ │ └── AppConfig.java // Spring配置
│ ├── controller
│ │ └── MainController.java // JavaFX控制器
│ ├── model
│ │ └── User.java // 数据模型
│ ├── repository
│ │ └── UserRepository.java // 数据访问
│ ├── service
│ │ └── UserService.java // 业务逻辑
│ └── view
│ └── StageManager.java // 视图管理
└── resources
├── fxml
│ └── main.fxml // UI布局
└── application.properties // Spring配置
1.2 MVP/MVVM 在整合应用中的实现
Spring Boot 与 JavaFX 的整合特别适合采用MVP或MVVM架构模式。
MVP (Model-View-Presenter) 实现
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Model │◄─────│ Presenter │◄─────│ View │
│ (Spring管理) │─────►│ (Spring管理) │─────►│ (JavaFX UI) │
└──────────────┘ └──────────────┘ └──────────────┘
实现要点:
- Model:由Spring管理的实体和业务逻辑
- View:JavaFX的UI组件,仅负责显示
- Presenter:连接Model和View,处理UI事件和数据转换
代码示例:
// Model (Spring管理)
@Service
public class UserService {
private final UserRepository repository;
@Autowired
public UserService(UserRepository repository) {
this.repository = repository;
}
public List<User> findAllUsers() {
return repository.findAll();
}
}
// Presenter (Spring管理)
@Component
public class UserPresenter {
private final UserService userService;
private UserView view;
@Autowired
public UserPresenter(UserService userService) {
this.userService = userService;
}
public void setView(UserView view) {
this.view = view;
}
public void loadUsers() {
List<User> users = userService.findAllUsers();
view.displayUsers(users);
}
}
// View (JavaFX)
public class UserViewController implements UserView {
@FXML
private TableView<User> userTable;
@Autowired
private UserPresenter presenter;
@FXML
public void initialize() {
presenter.setView(this);
}
@Override
public void displayUsers(List<User> users) {
userTable.getItems().setAll(users);
}
}
MVVM (Model-View-ViewModel) 实现
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Model │◄─────│ ViewModel │◄─────│ View │
│ (Spring管理) │─────►│ (Spring管理) │─────►│ (JavaFX UI) │
└──────────────┘ └──────────────┘ └──────────────┘
▲ │
│ ▼
┌──────────────┐
│ Data Binding │
└──────────────┘
实现要点:
- Model:由Spring管理的数据模型和业务逻辑
- View:JavaFX的UI组件
- ViewModel:由Spring管理,包含UI所需的数据和命令
- 绑定:JavaFX的属性绑定机制实现View与ViewModel的双向绑定
代码示例:
// ViewModel (Spring管理)
@Component
public class UserViewModel {
private final UserService userService;
private final ObservableList<User> users = FXCollections.observableArrayList();
private final StringProperty searchText = new SimpleStringProperty("");
@Autowired
public UserViewModel(UserService userService) {
this.userService = userService;
// 响应搜索文本变化
searchText.addListener((obs, oldValue, newValue) -> {
if (newValue != null) {
loadFilteredUsers(newValue);
}
});
}
public void loadUsers() {
List<User> userList = userService.findAllUsers();
users.setAll(userList);
}
private void loadFilteredUsers(String filter) {
List<User> filteredList = userService.findByNameContaining(filter);
users.setAll(filteredList);
}
// Getters for observable collections and properties
public ObservableList<User> getUsers() {
return users;
}
public StringProperty searchTextProperty() {
return searchText;
}
}
// View (JavaFX)
public class UserView {
@FXML
private TableView<User> userTable;
@FXML
private TextField searchField;
@Autowired
private UserViewModel viewModel;
@FXML
public void initialize() {
// 双向绑定搜索文本
searchField.textProperty().bindBidirectional(viewModel.searchTextProperty());
// 绑定表格数据
userTable.setItems(viewModel.getUsers());
// 初始加载数据
viewModel.loadUsers();
}
}
1.3 整合应用的启动流程设计
Spring Boot 与 JavaFX 的整合涉及到两个框架各自的启动流程,需要精心设计。
启动流程图
┌────────────────────────────────────┐
│ 1. Spring Boot 应用入口点 │
│ public static void main() │
└─────────────────┬──────────────────┘
│
▼
┌────────────────────────────────────┐
│ 2. 启动Spring容器 │
│ SpringApplication.run() │
└─────────────────┬──────────────────┘
│
▼
┌────────────────────────────────────┐
│ 3. Spring初始化完成 │
│ @PostConstruct │
└─────────────────┬──────────────────┘
│
▼
┌────────────────────────────────────┐
│ 4. 启动JavaFX应用 │
│ 启动JavaFX Application │
└─────────────────┬──────────────────┘
│
▼
┌────────────────────────────────────┐
│ 5. JavaFX应用初始化 │
│ Application.init() │
└─────────────────┬──────────────────┘
│
▼
┌────────────────────────────────────┐
│ 6. JavaFX UI线程启动 │
│ Application.start() │
└─────────────────┬──────────────────┘
│
▼
┌────────────────────────────────────┐
│ 7. 加载主场景 │
│ 加载FXML并显示主舞台 │
└────────────────────────────────────┘
标准启动代码示例
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
public class SpringBootJavaFxApplication extends Application {
private ConfigurableApplicationContext applicationContext;
@Override
public void init() {
// 在JavaFX线程初始化之前启动Spring
applicationContext = new SpringApplicationBuilder(MainConfig.class)
.run();
}
@Override
public void start(Stage primaryStage) {
// 从Spring容器中获取StageManager
StageManager stageManager = applicationContext.getBean(StageManager.class);
// 设置主舞台并显示主界面
stageManager.setPrimaryStage(primaryStage);
stageManager.showMainView();
}
@Override
public void stop() {
// 关闭Spring容器
applicationContext.close();
Platform.exit();
}
// 主入口
public static void main(String[] args) {
// 启动JavaFX应用
launch(args);
}
}
// Spring Boot配置类
@SpringBootApplication
public class MainConfig {
@Bean
public StageManager stageManager() {
return new StageManager();
}
}
// 舞台管理器
@Component
public class StageManager {
@Autowired
private ConfigurableApplicationContext springContext;
private Stage primaryStage;
public void setPrimaryStage(Stage primaryStage) {
this.primaryStage = primaryStage;
primaryStage.setTitle("Spring Boot + JavaFX应用");
}
public void showMainView() {
try {
// 创建FXML加载器
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"));
// 设置控制器工厂,使用Spring创建控制器实例
loader.setControllerFactory(springContext::getBean);
Parent root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
替代方案:保留Spring Boot的启动方式
@SpringBootApplication
public class MainApplication {
private ConfigurableApplicationContext applicationContext;
@Bean
public StageInitializer stageInitializer() {
return new StageInitializer();
}
public static void main(String[] args) {
Application.launch(JavaFxApplication.class, args);
}
}
// JavaFX应用类
public class JavaFxApplication extends Application {
private ConfigurableApplicationContext applicationContext;
@Override
public void init() {
applicationContext = SpringApplication.run(MainApplication.class);
}
@Override
public void start(Stage stage) {
applicationContext.publishEvent(new StageReadyEvent(stage));
}
@Override
public void stop() {
applicationContext.close();
Platform.exit();
}
static class StageReadyEvent extends ApplicationEvent {
public StageReadyEvent(Stage stage) {
super(stage);
}
public Stage getStage() {
return (Stage) getSource();
}
}
}
// 舞台初始化器
@Component
public class StageInitializer {
@Autowired
private ConfigurableApplicationContext applicationContext;
@EventListener
public void onStageReady(StageReadyEvent event) {
try {
Stage stage = event.getStage();
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"));
loader.setControllerFactory(applicationContext::getBean);
Parent root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 依赖注入机制
2.1 Spring 容器与 JavaFX 控制器的桥接
Spring 容器与 JavaFX 控制器的桥接是整合中最关键的环节之一,实现这两个框架的无缝连接。
基本桥接原理
┌─────────────────────┐ ┌─────────────────────┐
│ Spring 容器 │ │ JavaFX 系统 │
│ │ │ │
│ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ @Component │ │ │ │ FXML │ │
│ │ @Service │ │ │ │ │ │
│ │ @Repository │ │ │ │ │ │
│ └───────────────┘ │ │ └───────┬───────┘ │
│ ▲ │ │ │ │
└──────────┼──────────┘ └──────────┼──────────┘
│ │
│ ▼
│ ┌─────────────────────┐
│ │ FXMLLoader │
│ │ │
└─