通过debug深度解析xJavaFxTool的源码
背景
在上一篇的博客JavaFX爱好者看过来,这款工具值得拥有中,简单介绍了一款JavaFx开发框架xJavaFxTool。本次将采用debug的方式对xJavaFxTool进行调试,深度讲解重点模块的代码设计实现。希望通过本文的讲解,能够让您对xJavaFxTool有更深入的了解和认识。
一、项目主体结构
在这里再次重复一遍工程项目的主体结构,遵循MVC三层架构,在JavaFx中也是有相应的应用的,视图层采用Fxml来进行展示,控制层有对应的Contoller,每个视图层对象都会对应一个Controller,模型层对象这里采用了多个JavaBean和业务实体来组成,共同完成相应的逻辑功能封装。
xJavaFxTool ├─ images 项目截图 ├─ pom.xml maven配置文件 ├─ README.md 说明文件 ├─ src │ ├─ main │ │ ├─ java │ │ │ └─ com │ │ │ └─ xwintop │ │ │ └─ xJavaFxTool │ │ │ ├─ common 第三方工具类 │ │ │ ├─ controller javafx控制层 │ │ │ │ └─ index 首页控制层 │ │ │ ├─ model 基础bean类层 │ │ │ ├─ services 工具服务层 │ │ │ │ └─ index 首页工具服务层 │ │ │ ├─ utils 系统工具类 │ │ │ └─ view javafx视图层 │ │ │ └─ index 首页工具视图层 │ │ └─ resources │ │ ├─ com │ │ │ └─ xwintop │ │ │ └─ xJavaFxTool │ │ │ └─ fxmlView .fxml文件 │ │ ├─ config 配置文件 │ │ │ └─ toolFxmlLoaderConfiguration.xml 系统菜单加载配置文件 │ │ ├─ css 样式资源 │ │ ├─ images 图片资源 │ │ ├─ locale 国际化 │ │ ├─ banner.txt 启动banner图片 │ │ └─ logback.xml logback日志配置文件 │ └─ test 测试类 │ ├─ java │ └─ resources
二、Main.java 程序入口
public static void main(String[] args) { XJavaFxSystemUtil.initSystemLocal();//初始化本地语言 XJavaFxSystemUtil.addJarByLibs();//添加外部jar包 SplashScreen splashScreen = new SplashScreen() { @Override public String getImagePath() { return "/images/javafx.png"; } }; launch(Main.class, IndexView.class, splashScreen, args); }
1、本地语言设置
下面通过Debug来看看在这短短的几行代码中会包含那些操作。第一步需要进行本地语言设置,通过调用InitSystemLocal方法来实现。
2、加载外置的插件jar包
/** * @Title: addJarByLibs * @Description: 添加libs中jar包到系统中 */ public static void addJarByLibs() { PluginManager.getInstance().loadLocalPlugins(); try { // 系统类库路径 File libPath = new File("libs/"); // 获取所有的.jar和.zip文件 File[] jarFiles = libPath.listFiles( (dir, name) -> name.endsWith(".jar") ); if (jarFiles != null) { for (File file : jarFiles) { if (!PluginManageService.isPluginEnabled(file.getName())) { continue; } addJarClass(file); } } } catch (Exception e) { log.error("添加libs中jar包到系统中异常:", e); } }
这里需要注意的是,为了方便集中统一管理jar包,将外置的可运行的jar都放在libs目录下面,应用程序启动后在固定目录下读取指定jar,通过反射即可完成插件的调用。这也是该框架的一个亮点,在开发时大大减少了工作量,也降低了代码的复杂度。
通过上述代码可以看到,通过配置的插件json配置,插件读取器会自动将数据解析并绑定到相应的数据集合中。下图是解析出来的插件json文件。
private void addOrUpdatePlugin(PluginJarInfo pluginJarInfo, Consumer<PluginJarInfo> ifExists) { PluginJarInfo exists = getPlugin(pluginJarInfo.getJarName()); if (exists == null) { this.pluginList.add(pluginJarInfo); } else { ifExists.accept(exists); } }
加载已下载插件,并通过反射机制进行调用,核心代码如下:
至此主程序的驱动入口函数调试完成,最后两行代码是指定程序的启动窗口。关键代码如下:
launch(Main.class, IndexView.class, splashScreen, args);
IndexView是应用程序的入口窗口。splashScreen定义了启动页面,同时在启动页面中嵌入了一张本地图片。
三、IndexView解析
通常来说,一个IndexView包含了三个文件,一个是IndexView.java,在这个文件中绑定了相对应的页面配置fxml,controller是页面视图对象上所有操作逻辑的处理对象。核心的功能也是放在Controller对象中。
@Scope("prototype") @FXMLView(value = "/com/xwintop/xJavaFxTool/fxmlView/Index.fxml", bundle = "locale.Menu") public class IndexView extends AbstractFxmlView { public IndexView() throws Exception { //反射修改默认语言 ResourceBundle bundle = ResourceBundle.getBundle(this.getResourceBundle().get().getBaseBundleName(), Config.defaultLocale); FieldUtils.writeField(this,"bundle",Optional.ofNullable(bundle),true); GUIState.getStage().setTitle(bundle.getString("Title"));//修改标题国际化 } @Override public Parent getView() { JFXDecorator decorator = JavaFxViewUtil.getJFXDecorator(GUIState.getStage(),GUIState.getStage().getTitle() + Config.xJavaFxToolVersions,"/images/icon.jpg",super.getView()); decorator.setOnCloseButtonAction(() -> { if (AlertUtil.showConfirmAlert("确定要退出吗?")) { System.exit(0); } }); return decorator; } }
下面是通过场景编辑器打开Index.Fxml页面对象,可以看到这里的布局选择器采用的是:AnchorPane、BorderPane,中间采用的是TabPane,通过Tab页来切换功能。
在这里主要完成页面上相关菜单的加载和展示。
private void initView() { menuMap.put("toolsMenu", toolsMenu); menuMap.put("moreToolsMenu", moreToolsMenu); File libPath = new File("libs/"); // 获取所有的.jar和.zip文件 File[] jarFiles = libPath.listFiles((dir, name) -> name.endsWith(".jar")); if (jarFiles != null) { for (File jarFile : jarFiles) { if (!PluginManageService.isPluginEnabled(jarFile.getName())) { continue; } try { this.addToolMenu(jarFile); } catch (Exception e) { log.error("加载工具出错:", e); } } } }
通过以上的方法可以生成相应的菜单列表供调用。在默认的tab页面,前面可以看到有一个默认的记事本的页面。
public void addNodepadAction(ActionEvent event) { TextArea notepad = new TextArea(); notepad.setFocusTraversable(true); if (indexController.getSingleWindowBootCheckBox().isSelected()) { JavaFxViewUtil.getNewStage(indexController.getBundle().getString("addNodepad"), null, notepad); } else { Tab tab = new Tab(indexController.getBundle().getString("addNodepad")); tab.setContent(notepad); indexController.getTabPaneMain().getTabs().add(tab); if (event != null) { indexController.getTabPaneMain().getSelectionModel().select(tab); } } }
窗口的默认设置方法和参数可以参考以下的配置。
/** * 获取新窗口 **/ public static Stage getNewStage(String title, String iconUrl, Parent root) { double[] screenSize = JavaFxSystemUtil.getScreenSizeByScale(0.74, 0.8); Stage newStage = getNewStageNull(title, iconUrl, root, screenSize[0], screenSize[1], true, true, true); newStage.initModality(Modality.NONE); //newStage.setMaximized(false); newStage.show(); return newStage; }
四、总结
以上就是本文的主要内容,本文主要通过对代码进行debug的方式进行深入调试,详细讲解了在启动过程中,各业务逻辑模块是怎么工作的,插件的读取到菜单的生成,最重要的核心技术应该是用过JAVA的反射技术来实现动态注册及调用。心动不如行动,喜欢的朋友可以学习起来,给自己充电加油。
- 点赞
- 收藏
- 关注作者
评论(0)