springboot3.0自動配置
目標
本文主要介紹springboot3.0是如何創建一個可以進行自動配置的jar包的
自動配置的定義是,一個jar包里面定義了一些spring的bean,當導入這個jar包的時候會自動將這些bean導入進去
方法
創建 AutoConfiguration.imports 文件
創建目錄 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

在該文件里面寫入自動配置類的全限域名,例如:cn.hellozjf.game.pcr.common.CommonAutoConfiguration
創建自動配置類

代碼內容如下:
package cn.hellozjf.game.pcr.common;
import cn.hellozjf.game.pcr.common.config.WebDriverAutoConfiguration;
import cn.hellozjf.game.pcr.common.config.WebDriverConfig;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
@Import({WebDriverAutoConfiguration.class})
@EnableConfigurationProperties({WebDriverConfig.class})
public class CommonAutoConfiguration {
}
其中,WebDriverAutoConfiguration 定義了自動配置時會導入的 bean,WebDriverConfig 定義了自動配置時需要讀取的配置文件
WebDriverAutoConfiguration
這個類里面定義了各種需要導入的 bean
package cn.hellozjf.game.pcr.common.config;
import cn.hellozjf.game.pcr.common.service.IWebDriverService;
import cn.hellozjf.game.pcr.common.service.WebDriverServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
@AllArgsConstructor
public class WebDriverAutoConfiguration {
private final WebDriverConfig webDriverConfig;
@Bean
@ConditionalOnMissingBean(IWebDriverService.class)
public IWebDriverService webDriverService() {
return new WebDriverServiceImpl(webDriverConfig);
}
}
如上面的代碼所示,它會在構造的時候將 WebDriverConfig 配置信息全部導入進來,如果在初始化的時候發現沒有定義 IWebDriverService 這個 bean,那么就會實例化這個 bean
WebDriverConfig
它的代碼如下所示
package cn.hellozjf.game.pcr.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "webdriver")
public class WebDriverConfig {
private Boolean headless;
private String proxy;
private String driverKey;
private String driverValue;
}
它對應配置文件中的內容如下

IWebDriverService
這是 bean 的定義基類,代碼如下
package cn.hellozjf.game.pcr.common.service;
import org.openqa.selenium.WebElement;
import org.springframework.beans.factory.InitializingBean;
import java.util.List;
/**
* web瀏覽器操作服務
*/
public interface IWebDriverService extends InitializingBean {
/**
* 初始化
*/
void init();
/**
* 退出瀏覽器
*/
void quit();
/**
* 執行 JS 腳本
* @param script
*/
void executeScript(String script);
/**
* 通過執行 JS 腳本,獲取網頁元素
* @param script
* @return
*/
WebElement findByExecuteScript(String script);
/**
* 從網頁元素中,通過 JS 腳本獲取該元素里面的字符串數據
* @param webElement
* @param script
* @return
*/
String getStrByExecuteScript(WebElement webElement, String script);
/**
* 根據 CSS 選擇器查找網頁元素
* @param selector
* @return
*/
List<WebElement> findByCssSelector(String selector);
/**
* 在某個網頁元素下,根據 CSS 選擇器查找網頁元素
* @param webElement
* @param selector
* @return
*/
List<WebElement> findByCssSelector(WebElement webElement, String selector);
/**
* 根據 ID 選擇器查找網頁元素
* @param selector
* @return
*/
List<WebElement> findByIdSelector(String selector);
/**
* 在某個網頁元素下,根據 ID 選擇器查找網頁元素
* @param webElement
* @param selector
* @return
*/
List<WebElement> findByIdSelector(WebElement webElement, String selector);
/**
* 根據標簽查找網頁元素
* @param tag
* @return
*/
List<WebElement> findByTag(String tag);
/**
* 打開網頁
* @param url
*/
void openUrl(String url);
/**
* 判斷打開頁面操作是否完成
* @return
*/
boolean isOpenComplete();
/**
* 阻塞進程,直到頁面元素出現,或者超過timeout秒
* @param element
* @param timeout
*/
void waitUntilElementExist(String element, int timeout);
/**
* 不需要實現 afterPropertiesSet(),我也不知道這有啥用,照著抄就對了
* @throws Exception
*/
@Override
default void afterPropertiesSet() throws Exception {
}
}
WebDriverServiceImpl
這是 IWebDriverService 的實現類,內容如下
package cn.hellozjf.game.pcr.common.service;
import cn.hellozjf.game.pcr.common.config.WebDriverConfig;
import cn.hellozjf.game.pcr.common.util.OSUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.util.StringUtils;
import java.time.Duration;
import java.util.List;
@Slf4j
@RequiredArgsConstructor
public class WebDriverServiceImpl implements IWebDriverService {
private final WebDriverConfig webDriverConfig;
private boolean bInit = false;
private WebDriver driver;
private JavascriptExecutor js;
@Override
public void init() {
if (bInit) {
log.error("WebDriverService已經初始化");
return;
}
// 設置 ChromeDriver 的路徑
System.setProperty(webDriverConfig.getDriverKey(), webDriverConfig.getDriverValue());
ChromeOptions options = new ChromeOptions();
// 獲取 driver 和 js 變量
if (StringUtils.hasLength(webDriverConfig.getProxy())) {
// 說明需要走代理
options.addArguments("--proxy-server=" + webDriverConfig.getProxy());
}
// 設置無界面
if (webDriverConfig.getHeadless()) {
options.addArguments("--headless");
}
if (OSUtil.isLinux()) {
options.addArguments("--no-sandbox"); // 適用于Linux環境
options.addArguments("--disable-dev-shm-usage"); // 適用于Linux環境
}
// 創建 WebDriver 實例
driver = new ChromeDriver(options);
js = (JavascriptExecutor) driver;
bInit = true;
}
@Override
public void quit() {
checkInit();
driver.quit();
bInit = false;
}
@Override
public void executeScript(String script) {
checkInit();
js.executeScript(script);
}
@Override
public WebElement findByExecuteScript(String script) {
checkInit();
return (WebElement) js.executeScript(script);
}
@Override
public String getStrByExecuteScript(WebElement webElement, String script) {
return (String) js.executeScript(script, webElement);
}
@Override
public List<WebElement> findByCssSelector(String selector) {
checkInit();
List<WebElement> webElementList = driver.findElements(By.cssSelector(selector));
return webElementList;
}
@Override
public List<WebElement> findByCssSelector(WebElement webElement, String selector) {
// todo 為啥這里不用 checkInit?
return webElement.findElements(By.cssSelector(selector));
}
@Override
public List<WebElement> findByIdSelector(String selector) {
checkInit();
List<WebElement> webElementList = driver.findElements(By.id(selector));
return webElementList;
}
@Override
public List<WebElement> findByIdSelector(WebElement webElement, String selector) {
// todo 為啥這里不用 checkInit?
return webElement.findElements(By.id(selector));
}
@Override
public List<WebElement> findByTag(String tag) {
checkInit();
List<WebElement> webElementList = driver.findElements(By.tagName(tag));
return webElementList;
}
@SneakyThrows
@Override
public void openUrl(String url) {
checkInit();
// 打開網頁
driver.get(url);
while (!isOpenComplete()) {
// 頁面還沒有打開,等待 1 秒
Thread.sleep(1000);
}
log.debug("open {} success", url);
}
@Override
public boolean isOpenComplete() {
// 使用 JavaScript 檢查頁面加載狀態,確保網頁打開之后才執行后續的操作
String readyState = (String) js.executeScript("return document.readyState");
return "complete".equals(readyState);
}
@Override
public void waitUntilElementExist(String element, int timeout) {
checkInit();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(timeout));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(element)));
}
private void checkInit() {
if (!bInit) {
throw new RuntimeException("WebDriverService還未初始化");
}
}
}
流程說明
當我在其他項目中導入了這個 jar

那么這個 jar 包里面的 META-INF/spring/AutoConfiguration.imports 就會被 spring 容器掃描到,然后就回去加載自動配置類CommonAutoConfiguration
因為CommonAutoConfigruation引用了WebDriverConfig,所以就會讀取application.yml中的配置
因為CommonAutoConfiguration導入了WebDriverAutoConfiguration,所以就會加載WebDriverAutoConfiguration,并且創建出一個IWebDriverService的bean,這個bean的內容會根據配置進行初始化
最后其它項目就能獲取到這個bean,并進行使用了
IWebDriverService webDriverService = SpringUtil.getBean(IWebDriverService.class);
webDriverService.init();
后話
本文是我在等待gradle下載依賴時候編寫,依賴下載太耗時間了,等的我心很煩,所以本文寫的也就意思一下
這個自動配置我是參考pigx實現的,有興趣的讀者還是研究pigx的源碼更好,我的代碼也就我自己的項目用用,寫的對不對也不知道呢

浙公網安備 33010602011771號