Balidrop-spring-boot-browser讲解
一、破解 jxbrowser 4 Google chromium
static {
try {
Field e = bb.class.getDeclaredField("e");
e.setAccessible(true);
Field f = bb.class.getDeclaredField("f");
f.setAccessible(true);
Field modifersField = Field.class.getDeclaredField("modifiers");
modifersField.setAccessible(true);
modifersField.setInt(e, e.getModifiers() & ~Modifier.FINAL);
modifersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
e.set(null, new BigInteger("1"));
f.set(null, new BigInteger("1"));
modifersField.setAccessible(false);
} catch (Exception e1) {
e1.printStackTrace();
}
}
二、启动springboot且设置主窗口的大小和初始化用户的信息
-
初始化 initialize方法
public static void main(String[] args) { //设置加载等待页面 LoadSplashScreen splashScreen = new LoadSplashScreen(); //默认加载主 窗口 MainView.class --> launchApp(BalidropBrowserApp.class, MainView.class, splashScreen, new String[]{}); } @Override public void start(Stage stage) throws Exception { Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds(); stage.setX(primaryScreenBounds.getMinX()); stage.setY(primaryScreenBounds.getMinY()); stage.setWidth(primaryScreenBounds.getWidth()); stage.setHeight(primaryScreenBounds.getHeight()); super.start(stage); } @Override public void init() throws Exception { super.init(); CustomerHandle.getCustomer(); }
三、 针对主view的 控制器 MainStageController
1、初始化默认的 selectedTab
CreateTabController.selectedTab = defaultTab;
2、获取之前登陆的信息登陆系统
new Thread(new Runnable() {
@Override
public void run() {
if (CustomerHandle.getCustomer() != null) {
String isloginUrl = SERVER_HOST + "/subject/isLogin?needMenu=false&_=" + Math.random();
ResponseJsonData requestServer = BrowerUtils.requestServer(isloginUrl, Method.GET, null,
ResponseJsonData.class);
if (!requestServer.isFlag()) {
CustomerHandle.setCustomer(new Customer());
// 开始进行解析
MainStageController.this.loginStatusInfo();
}
}
}
}).start();
3、设置 主页面的 链接框的宽度
//设置searchText宽度
searchText.setPrefWidth(new Double(Double.MAX_VALUE).intValue());
4、设置 谷歌 浏览器 调试端口
//设置searchText宽度
BrowserPreferences.setChromiumSwitches("--remote-debugging-port=9222");
5、设置 tabls的关闭策略
tabs.setTabClosingPolicy(TabClosingPolicy.SELECTED_TAB);
6、设置默认一个的tab是否可关闭
defaultTab.setClosable(false);
6、初始化谷歌浏览器内核
// 1. 创建浏览器上下文,数据存放路径
BROWSERCONTEXT = new BrowserContext(
new BrowserContextParams(new File(BrowerUtils.getTmpDir(), "balidrop").getAbsolutePath()));
BROWSERCONTEXT.getNetworkService().setCertificateVerifier(params -> CertificateVerifyResult.OK);
// 2. 创建浏览器
BrowserView browserView = new BrowserView(new Browser(BROWSERCONTEXT));
7、设置border panel面板中的 center布局为 browerView
defaultBorderPane.setCenter(browserView);
8、加载之前的cookie信息,这样保证浏览器在关闭的时候cookie功能可用
// 加载cookie
CookieStorage cookieStorage = browserView.getBrowser().getCookieStorage();
BrowerUtils.addCookieByHost(cookieStorage);
9、设置default tab的主页
Platform.runLater(() -> { browserView.getBrowser().loadURL(MainStageController.this.HOME_URL);
});
10、设置 defaultTab 的监听 ,因为页面的头信息都是开启一个的时候动态的渲染出来的
defaultTab.setOnSelectionChanged(event -> {
// 1. 将当前选中的tab页的头信息设置到选中的tab页中
Tab source = CreateTabController.selectedTab;
BorderPane sourceContent = (BorderPane) source.getContent();
Tab target = (Tab) event.getTarget();
BorderPane targetContent = (BorderPane) target.getContent();
targetContent.setTop(sourceContent.getTop());
// 2. 更换选中的tab
CreateTabController.selectedTab = target;
// 3. 开发产品按钮是否显示
BrowerUtils.showOrHiddenButton(targetContent, mainStageController);
});
11、初始化browser
一、 设置页面中的超链接的方式
browserView.getBrowser().setPopupHandler(popupParams -> (a, b) -> Platform.runLater(() -> {
CreateTabController createTabController = new CreateTabController(this);
createTabController.addTab(a);
}));
二、设置是否使用cookie的信息的功能
browserView.getBrowser().getContext().getNetworkService().setNetworkDelegate(new DefaultNetworkDelegate() {
@Override
public boolean onCanSetCookies(String url, List cookies) {
return true;
}
@Override
public boolean onCanGetCookies(String url, List cookies) {
return true;
}
});
三、设置alert弹窗的逻辑
browserView.getBrowser().setDialogHandler(new DefaultDialogHandler(browserView) {
@Override
public void onAlert(DialogParams params) {
Platform.runLater(() -> {
BrowerUtils.alertDialog(AlertType.INFORMATION, params.getMessage());
});
}
});
四、设置 border 的document的加载回调, 根据链接的匹配行,向页面注入自己的脚本
MainStageController mainStageController = this;
browserView.getBrowser().addLoadListener(new LoadAdapter() {
@Override
public void onStartLoadingFrame(StartLoadingEvent arg0) {
if (arg0.isMainFrame() && (!arg0.isSameDocument())) {
logger.info("## start loading...");
}
}
@Override
public void onDocumentLoadedInMainFrame(LoadEvent event) {
注入页面的java对象为 JavaApp
JSValue executeScriptResult = browserView.getBrowser().executeJavaScriptAndReturnValue("window");
executeScriptResult.asObject().setProperty("JavaApp", MainStageController.this);
try {
// 显示链接
URI rUri = new URI(browserView.getBrowser().getURL());
String currentUrl = browserView.getBrowser().getURL();
logger.info("## currentUrl:" + currentUrl);
// 处理不打开tab页进入详情页面
BrowerUtils.showOrHiddenButton(mainStageController, currentUrl);
//开始向页面注入自己的脚本信息,
String executeJs = JSHandle.findExecuteJSByHost(rUri.getHost());
if (StringUtils.isNotBlank(executeJs)) {
browserView.getBrowser().executeJavaScript(executeJs);
}
// 保存cookie
List allCookies = browserView.getBrowser().getCookieStorage().getAllCookies();
Platform.runLater(() -> {
String title = browserView.getBrowser().getTitle();
if (title.length() > 10) {
title = title.substring(0, 10) + "...";
}
tab.setText(title);
BrowerUtils.setCookie2File(allCookies);
});
} catch (URISyntaxException e) {
logger.error("## onDocumentLoadedInMainFrame error", e);
}
}
});
五、打印 border 的调试端口信息
logger.info("## dubug地址:" + browserView.getBrowser().getRemoteDebuggingURL());
六、设置 F12监听 开启 浏览器的调试
private void addKeyEventsHandler(BrowserView browserView, Tab tab) {
browserView.setKeyEventsHandler(handler -> {
if ("F12".equalsIgnoreCase(handler.getCode().getName())) {
Platform.runLater(() -> {
int smallHeight = 350;
int bigHeight = 600;
BorderPane content = (BorderPane) CreateTabController.selectedTab.getContent();
if(content.getBottom() != null){
logger.info("## 已打开了,关闭debug");
content.setBottom(null);
return;
}
final BrowserView br;
if (content.getUserData() == null) {
br = new BrowserView(new Browser(BrowserType.HEAVYWEIGHT, BROWSERCONTEXT));
br.getBrowser().loadURL(browserView.getBrowser().getRemoteDebuggingURL());
br.setPrefHeight(smallHeight);
content.setUserData(br);
} else {
br = (BrowserView) content.getUserData();
br.setPrefHeight(smallHeight);
}
BorderPane borderPane = new BorderPane();
borderPane.setCenter(br);
HBox hBox = new HBox();
hBox.setAlignment(Pos.CENTER_RIGHT);
Text reSizeText = new Text("放大");
reSizeText.setFill(Color.GREEN);
reSizeText.setCursor(Cursor.HAND);
hBox.setStyle("-fx-font-size:14px;-fx-font-weight:bold");
HBox.setMargin(reSizeText, new Insets(0, 20, 0, 0));
reSizeText.setOnMouseClicked((EventHandler) event -> {
if(br.getPrefHeight() < bigHeight) {
br.setPrefHeight(bigHeight);
reSizeText.setText("恢复");
}else{
br.setPrefHeight(smallHeight);
reSizeText.setText("放大");
}
});
hBox.getChildren().add(reSizeText);
//关闭按钮
Text closeText = new Text("关闭");
closeText.setFill(Color.RED);
closeText.setCursor(Cursor.HAND);
HBox.setMargin(closeText, new Insets(0, 10, 0, 0));
closeText.setOnMouseClicked((EventHandler) event -> content.setBottom(null));
hBox.getChildren().add(closeText);
borderPane.setTop(hBox);
content.setBottom(borderPane);
});
return true;
}
return false;
});
}
七、设置 tab的右键 关闭的逻辑
MenuItem closeAllMenuItem = new MenuItem("Close All");
closeAllMenuItem.setOnAction(event -> {
// 关闭所有的,将第一个进行导航到首页
if (tabs.getTabs().size() > 1) {
BorderPane content = (BorderPane) defaultTab.getContent();
BrowserView browserView1 = (BrowserView) content.getCenter();
browserView1.getBrowser().loadURL(HOME_URL);
tabs.getTabs().retainAll(Lists.newArrayList(defaultTab));
}
});
tab.setContextMenu(new javafx.scene.control.ContextMenu(closeAllMenuItem));
12、 设置用户信息的展示
public void loginStatusInfo() {
String loginImgUrl = "static/4.png";
String customerInfo = "Log in";
if (CustomerHandle.isLogin()) {
customerInfo = CustomerHandle.getCustomer().getNickName();
loginImgUrl = "static/4-1.png";
}
// 统一设置登录信息【设置选中的tab页的信息】
BorderPane borderPane = (BorderPane) CreateTabController.selectedTab.getContent();
GridPane gridPane = (GridPane) borderPane.getTop();
HBox hbox = (HBox) gridPane.getChildren().get(1);
// 2.设置图片
ImageView loginImageView = (ImageView) hbox.getChildren().get(3);
loginImageView.setImage(new Image(new ClassPathResource(loginImgUrl).getPath()));
// 3.设置用户信息
Text customerInfoText = (Text) hbox.getChildren().get(4);
customerInfoText.setText(customerInfo);
}
13、重新定义主窗口的关闭逻辑,需要关闭 browserView
GUIState.getStage().setOnCloseRequest(event -> {
// 关闭窗口必须开启一个线程进行关闭
new Thread(() -> browserView.getBrowser().dispose(true)).start();
});
14、显示 测试环境和线上环境的主名称
// 重新定义app名称
GUIState.getStage().setOnShown(event -> Platform.runLater(() -> {
// 环境
String env = BrowerUtils.getActiveProfile();
String versionName = "测试环境";
if ("prod".equalsIgnoreCase(env)) {
versionName = "";
mainStageController.searchText.setVisible(false);
}else{
mainStageController.searchText.setVisible(true);
}
GUIState.getStage().setTitle(BrowerUtils.getAppName() + " " + BrowerUtils.getAppVersionDesc() + " "
+ BrowerUtils.getAppVersion() + " " + versionName);
}));
2、监听JS中的 提交事件 ,当页面中注入的JS脚本的点击 产品开发后,
public void executeAction4ProductList() {
MainStageController mainStageController = this;
Platform.runLater(new Runnable() {
@Override
public void run() {
// 操作前请先登录
boolean login = CustomerHandle.isLogin();
if (!login) { // 弹出登录框
showLogin();
return;
}
// 开始调用上传Service进行上传
try {
mainStageController.showLoading();
BorderPa ne content = (BorderPane) CreateTabController.selectedTab.getContent();
BrowserView browserView = (BrowserView) content.getCenter();
JSValue executeScriptResult = browserView.getBrowser()
.executeJavaScriptAndReturnValue("executeProductListWork()");
if (StringUtils.isNotBlank(executeScriptResult.getStringValue())) {
logger.info("productList....." + executeScriptResult.getStringValue());
ProductSaveDto productSaveDto = new Gson().fromJson(executeScriptResult.getStringValue(),
ProductSaveDto.class);
DataGrid dataGrid = BrowerUtils.requestServer4RequestBody(SERVER_HOST + "/products/",
productSaveDto, DataGrid.class);
final String message;
final AlertType alertType;
if (dataGrid.isFlag()) {
if (StringUtils.isBlank(dataGrid.getMsg())) {
message = "Successful operation";
} else {
message = dataGrid.getMsg();
}
alertType = AlertType.INFORMATION;
} else {
alertType = AlertType.ERROR;
if (StringUtils.isBlank(dataGrid.getMsg())) {
message = "Failed operation";
} else {
message = dataGrid.getMsg();
}
}
mainStageController.hideLoading();
BrowerUtils.alertDialog(alertType, message);
} else {
mainStageController.hideLoading();
}
} catch (Exception exception) {
mainStageController.hideLoading();
logger.error(exception.getMessage(), exception);
BrowerUtils.alertDialog(AlertType.ERROR, "Parameter error:" + exception.getMessage());
}
}
});
}
public void executeAction4PurchasingCart() {
MainStageController mainStageController = this;
Platform.runLater(new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
// 操作前请先登录
boolean login = CustomerHandle.isLogin();
if (!login) { // 弹出登录框
showLogin();
return;
}
try {
mainStageController.showLoading();
BorderPane content = (BorderPane) CreateTabController.selectedTab.getContent();
BrowserView browserView = (BrowserView) content.getCenter();
JSValue executeScriptResult = browserView.getBrowser()
.executeJavaScriptAndReturnValue("executePurchasingCartWork()");
// 开始调用上传Service进行上传
if (StringUtils.isNotBlank(executeScriptResult.getStringValue())) {
logger.info("purchars....." + executeScriptResult.getStringValue());
// 开始保存代购商品
Map agentParams = new Gson().fromJson(executeScriptResult.getStringValue(),
Map.class);
ResponseJsonData responseJsonData = BrowerUtils.requestServer(SERVER_HOST + "/agentOrder/save",
Method.POST, agentParams, ResponseJsonData.class);
final String message;
final AlertType alertType;
if (responseJsonData.isFlag()) {
message = "Successful operation";
alertType = AlertType.INFORMATION;
} else {
alertType = AlertType.ERROR;
message = "Failed operation";
}
mainStageController.hideLoading();
BrowerUtils.alertDialog(alertType, message);
} else {
mainStageController.hideLoading();
}
} catch (Exception exception) {
mainStageController.hideLoading();
logger.error(exception.getMessage(), exception);
BrowerUtils.alertDialog(AlertType.ERROR, "Parameter error:" + exception.getMessage());
}
}
});
}
3、以淘宝为案例 说明 代购和选品的 逻辑,其中的 JavaApp 为事先注入进来的
function rendererBaldripBtn(selector){
var divElement = document.createElement("div");
var productListBtn= document.createElement("button");
productListBtn.innerText="Develop Product";
productListBtn.style="padding:10px 0px;text-align:center;background-color:#EA0627;border-radius:4px;color:white;width:160px;font-size:18px;border:0px;margin:0px 20px 20px 0px;cursor:pointer;"
productListBtn.onclick=function(){
JavaApp.executeAction4ProductList();
}
var purcharBtn = document.createElement("button");
purcharBtn.innerText="Purchase Now";
purcharBtn.style="padding:10px 0px;text-align:center;background-color:#0F83EA;border-radius:4px;color:white;width:160px;font-size:18px;border:0px;cursor:pointer;"
purcharBtn.onclick=function(){
JavaApp.executeAction4PurchasingCart();
}
document.querySelector(selector).innerHTML = "";
document.querySelector(selector).appendChild(divElement);
divElement.appendChild(productListBtn);
divElement.appendChild(purcharBtn);
}