Java如何实现自定义Getter消除Selenium POM中的重复元素查找代码
最优实现方案
针对需要消除元素获取方法重复代码、实现类似Lombok @Getter 能力的需求,有两类成熟的实现方案,按适配程度排序如下:
方案1:编译期自定义注解处理器(仿Lombok逻辑,最高匹配需求)
该方案和Lombok实现原理完全一致,编译阶段自动生成对应元素获取方法,无运行时性能损耗,使用体验和原生@Getter完全一致。
实现步骤
- 定义两个SOURCE级别的标记注解,分别对应单个元素和集合元素的获取方法生成规则:
import java.lang.annotation.*; // 标记生成单个WebElement获取方法的By字段 @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface FindElementGetter { // 自定义生成的方法名,不填则默认取字段名去掉Selector后缀,如emailInputSelector生成emailInput() String value() default ""; } // 标记生成List<WebElement>获取方法的By字段 @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface FindElementsGetter { String value() default ""; }
- 编写自定义注解处理器,继承
javax.annotation.processing.AbstractProcessor,在process方法中完成以下逻辑:
- 扫描所有标注了上述两个注解的By类型字段
- 校验当前类是否存在WebDriver类型的成员变量
- 按照命名规则生成对应的public方法,方法体固定为
return driver.findElement(对应字段名)或return driver.findElements(对应字段名) - 将生成的方法写入对应类的字节码中
可配合Google AutoService自动注册注解处理器,无需手动维护META-INF配置文件。
- 改造后的业务页面对象代码可简化为:
public class SigninPage { WebDriver driver; @FindElementGetter private By emailInputSelector = By.id("user_email"); @FindElementGetter private By passwordInputSelector = By.id("user_password"); @FindElementGetter private By signinBtnSelector = By.xpath("//input[@value='Log In']"); @FindElementGetter private By errorDivSelector = By.xpath("//div[@role='alert']"); @FindElementsGetter private By headerLinksSelector = By.className("nav-link"); public SigninPage(WebDriver driver) { this.driver = driver; } // 所有元素获取方法编译时自动生成,无需手动编写 }
优缺点
- 优点:完全符合
@Getter使用习惯,无运行时开销,代码简洁度最高 - 缺点:需要额外开发注解处理器,调试成本略高,和Lombok共用时需注意配置注解处理器执行顺序避免冲突
方案2:轻量运行期方案(低开发成本,可快速落地)
如果不想投入成本开发自定义注解处理器,可采用以下两种落地成本极低的替代方案:
子方案2.1 抽象父类封装通用逻辑
抽取通用页面对象父类,封装元素获取逻辑,减少重复方法体代码:
// 通用父类 public abstract class BasePage { protected WebDriver driver; public BasePage(WebDriver driver) { this.driver = driver; } protected WebElement getElement(By selector) { return driver.findElement(selector); } protected List<WebElement> getElements(By selector) { return driver.findElements(selector); } } // 改造后的登录页面类 public class SigninPage extends BasePage { private By emailInputSelector = By.id("user_email"); private By passwordInputSelector = By.id("user_password"); private By signinBtnSelector = By.xpath("//input[@value='Log In']"); private By errorDivSelector = By.xpath("//div[@role='alert']"); private By headerLinksSelector = By.className("nav-link"); public SigninPage(WebDriver driver) { super(driver); } public WebElement emailInput() { return getElement(emailInputSelector); } public WebElement passwordInput() { return getElement(passwordInputSelector); } public WebElement signinBtn() { return getElement(signinBtnSelector); } public WebElement errorDiv() { return getElement(errorDivSelector); } public List<WebElement> headerElement() { return getElements(headerLinksSelector); } }
子方案2.2 Selenium原生PageFactory方案
使用Selenium自带的@FindBy注解配合PageFactory初始化,直接省去所有方法定义:
import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class SigninPage { WebDriver driver; @FindBy(id = "user_email") private WebElement emailInput; @FindBy(id = "user_password") private WebElement passwordInput; @FindBy(xpath = "//input[@value='Log In']") private WebElement signinBtn; @FindBy(xpath = "//div[@role='alert']") private WebElement errorDiv; @FindBy(className = "nav-link") private List<WebElement> headerElement; public SigninPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); } // 配合Lombok @Getter注解可自动生成所有元素的get方法,无需手动编写 }
优缺点
- 优点:开发成本极低,无需额外组件开发,可快速落地
- 缺点:要么仍需手动编写方法定义,要么依赖运行时代理有轻微性能损耗
内容的提问来源于stack exchange,提问作者keemahs
相关产品推荐
相关产品推荐

