Implementation:SeleniumHQ Selenium CacheLookup Annotation
| Knowledge Sources | |
|---|---|
| Domains | Test_Design, Performance, Browser_Automation |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Concrete tool for enabling element caching on PageFactory-managed WebElement fields provided by the Selenium Support library.
Description
@CacheLookup is a marker annotation (no parameters) applied to WebElement or List<WebElement> fields alongside @FindBy. It is retained at runtime (@Retention(RetentionPolicy.RUNTIME)) and targets fields only (@Target(ElementType.FIELD)). When DefaultElementLocatorFactory creates a DefaultElementLocator for a field, the Annotations helper class checks for the presence of @CacheLookup via field.isAnnotationPresent(CacheLookup.class) and sets the shouldCache flag accordingly. The DefaultElementLocator then stores resolved elements in its cachedElement or cachedElementList instance variables after the first successful lookup.
Usage
Add @CacheLookup to @FindBy-annotated fields for static elements that do not change after page load. Omit it for dynamic elements that may be added, removed, or replaced in the DOM.
Code Reference
Source Location
- Repository: Selenium
- File: java/src/org/openqa/selenium/support/CacheLookup.java (L29-31)
- File: java/src/org/openqa/selenium/support/pagefactory/DefaultElementLocator.java (L32-104)
Signature
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CacheLookup {
// Marker annotation -- no elements
}
The caching is implemented in DefaultElementLocator:
public class DefaultElementLocator implements ElementLocator {
private final SearchContext searchContext;
private final boolean shouldCache;
private final By by;
private WebElement cachedElement;
private List<WebElement> cachedElementList;
public DefaultElementLocator(SearchContext searchContext, Field field) {
this(searchContext, new Annotations(field));
}
public DefaultElementLocator(SearchContext searchContext, AbstractAnnotations annotations) {
this.searchContext = searchContext;
this.shouldCache = annotations.isLookupCached();
this.by = annotations.buildBy();
}
@Override
public WebElement findElement() {
if (cachedElement != null && shouldCache()) {
return cachedElement;
}
WebElement element = searchContext.findElement(by);
if (shouldCache()) {
cachedElement = element;
}
return element;
}
@Override
public List<WebElement> findElements() {
if (cachedElementList != null && shouldCache()) {
return cachedElementList;
}
List<WebElement> elements = searchContext.findElements(by);
if (shouldCache()) {
cachedElementList = elements;
}
return elements;
}
protected boolean shouldCache() {
return shouldCache;
}
}
Import
import org.openqa.selenium.support.CacheLookup;
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| (none) | - | - | Marker annotation with no parameters |
Outputs
| Name | Type | Description |
|---|---|---|
| cached proxy behavior | WebElement proxy | Proxy backed by DefaultElementLocator that caches the resolved element after first lookup; subsequent calls return the cached reference without querying the DOM |
Usage Examples
Static vs Dynamic Elements
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.PageFactory;
public class HomePage {
private WebDriver driver;
// Static element -- safe to cache; never changes after page load
@FindBy(id = "main-nav")
@CacheLookup
private WebElement navigation;
// Static element -- safe to cache
@FindBy(css = "header h1")
@CacheLookup
private WebElement pageTitle;
// Dynamic element -- do NOT cache; appears after AJAX call
@FindBy(css = ".notification-badge")
private WebElement notificationBadge;
// Dynamic element -- do NOT cache; content updates via JavaScript
@FindBy(id = "live-feed")
private WebElement liveFeed;
public HomePage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
public String getPageTitle() {
return pageTitle.getText(); // Uses cached element reference
}
public String getNotificationCount() {
return notificationBadge.getText(); // Re-queries DOM every call
}
}
Caching with List<WebElement>
import java.util.List;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.CacheLookup;
public class NavigationPage {
// Static list -- the nav links are always present
@FindBy(css = "#main-nav a")
@CacheLookup
private List<WebElement> navLinks;
public int getNavLinkCount() {
return navLinks.size(); // Uses cached list after first access
}
}