0
# Context Keys and Scoping
1
2
Context keys provide type-safe indexing for values stored in contexts, while scopes manage the lifecycle of context attachments with automatic cleanup.
3
4
## Context Keys
5
6
### Creating Context Keys
7
8
Creates a new context key with an optional debug name.
9
10
```java { .api }
11
static <T> ContextKey<T> named(String name);
12
```
13
14
**Parameters:**
15
- `name` - Debug name for the key (does not affect behavior)
16
17
**Returns:** A new ContextKey instance
18
19
**Usage Example:**
20
```java
21
// Create typed context keys
22
private static final ContextKey<String> USER_ID_KEY = ContextKey.named("userId");
23
private static final ContextKey<Integer> REQUEST_COUNT_KEY = ContextKey.named("requestCount");
24
private static final ContextKey<List<String>> TAGS_KEY = ContextKey.named("tags");
25
26
// Use keys for type-safe storage and retrieval
27
Context context = Context.current()
28
.with(USER_ID_KEY, "user123")
29
.with(REQUEST_COUNT_KEY, 42)
30
.with(TAGS_KEY, Arrays.asList("tag1", "tag2"));
31
32
// Type-safe retrieval
33
String userId = context.get(USER_ID_KEY); // String
34
Integer count = context.get(REQUEST_COUNT_KEY); // Integer
35
List<String> tags = context.get(TAGS_KEY); // List<String>
36
```
37
38
### Key Identity and Comparison
39
40
Context keys are compared by reference equality, not by name. Each call to `named()` creates a distinct key.
41
42
```java
43
// These are different keys, even with same name
44
ContextKey<String> key1 = ContextKey.named("user");
45
ContextKey<String> key2 = ContextKey.named("user");
46
47
Context ctx = Context.current().with(key1, "alice");
48
String value1 = ctx.get(key1); // Returns "alice"
49
String value2 = ctx.get(key2); // Returns null - different key!
50
```
51
52
**Best Practices:**
53
- Store keys as `static final` fields
54
- Use descriptive names for debugging
55
- Create keys once and reuse them
56
57
```java
58
public class UserContext {
59
// Good: single key instance shared across usage
60
private static final ContextKey<String> USER_ID_KEY = ContextKey.named("userId");
61
62
public static Context withUserId(String userId) {
63
return Context.current().with(USER_ID_KEY, userId);
64
}
65
66
public static String getUserId() {
67
return Context.current().get(USER_ID_KEY);
68
}
69
}
70
```
71
72
## Scope Management
73
74
### Scope Interface
75
76
Represents a mounted context that must be closed to restore the previous context.
77
78
```java { .api }
79
interface Scope extends AutoCloseable {
80
static Scope noop();
81
void close();
82
}
83
```
84
85
### No-op Scope
86
87
Returns a scope that does nothing when closed.
88
89
```java { .api }
90
static Scope noop();
91
```
92
93
Used internally when attaching a context that's already current.
94
95
**Usage Example:**
96
```java
97
// Conditional scope creation
98
public Scope attachIfNeeded(Context context) {
99
if (Context.current() != context) {
100
return context.makeCurrent();
101
} else {
102
return Scope.noop(); // No change needed
103
}
104
}
105
```
106
107
### Closing Scopes
108
109
Closes the scope and restores the previous context.
110
111
```java { .api }
112
void close();
113
```
114
115
**Usage with try-with-resources:**
116
```java
117
Context newContext = Context.current().with(USER_KEY, user);
118
119
try (Scope scope = newContext.makeCurrent()) {
120
// Context is active here
121
performOperation();
122
} // Scope automatically closed, previous context restored
123
```
124
125
**Manual closing (not recommended):**
126
```java
127
Context newContext = Context.current().with(USER_KEY, user);
128
Scope scope = newContext.makeCurrent();
129
try {
130
performOperation();
131
} finally {
132
scope.close(); // Must close manually
133
}
134
```
135
136
## Scope Lifecycle Examples
137
138
### Basic Scope Usage
139
140
```java
141
private static final ContextKey<String> OPERATION_KEY = ContextKey.named("operation");
142
143
public void performUserOperation(String userId, String operation) {
144
Context operationContext = Context.current()
145
.with(USER_ID_KEY, userId)
146
.with(OPERATION_KEY, operation);
147
148
try (Scope scope = operationContext.makeCurrent()) {
149
// Context is available to all called methods
150
logOperationStart();
151
executeOperation();
152
logOperationEnd();
153
} // Previous context automatically restored
154
}
155
156
private void logOperationStart() {
157
String userId = Context.current().get(USER_ID_KEY);
158
String operation = Context.current().get(OPERATION_KEY);
159
logger.info("Starting {} for user {}", operation, userId);
160
}
161
```
162
163
### Nested Scopes
164
165
```java
166
public void processRequest(String requestId) {
167
Context requestContext = Context.current().with(REQUEST_ID_KEY, requestId);
168
169
try (Scope requestScope = requestContext.makeCurrent()) {
170
logger.info("Processing request: {}", requestId);
171
172
for (String userId : getUsers()) {
173
Context userContext = Context.current().with(USER_ID_KEY, userId);
174
175
try (Scope userScope = userContext.makeCurrent()) {
176
// Both request ID and user ID available
177
processUser();
178
} // User context ends, request context continues
179
}
180
181
} // Request context ends, original context restored
182
}
183
```
184
185
### Error Handling with Scopes
186
187
Scopes are automatically closed even when exceptions occur:
188
189
```java
190
public void riskyOperation() {
191
Context context = Context.current().with(OPERATION_KEY, "risky");
192
193
try (Scope scope = context.makeCurrent()) {
194
// Context is available
195
performRiskyOperation();
196
197
if (someCondition) {
198
throw new RuntimeException("Operation failed");
199
}
200
201
} // Scope closed even if exception thrown
202
catch (RuntimeException e) {
203
// Previous context is already restored
204
logger.error("Operation failed", e);
205
throw e;
206
}
207
}
208
```
209
210
## Advanced Scope Patterns
211
212
### Conditional Context Application
213
214
```java
215
public void conditionalContext(String userId) {
216
boolean needsUserContext = userId != null && !userId.isEmpty();
217
218
Scope scope = needsUserContext
219
? Context.current().with(USER_ID_KEY, userId).makeCurrent()
220
: Scope.noop();
221
222
try (scope) {
223
performOperation();
224
}
225
}
226
```
227
228
### Scope Delegation
229
230
```java
231
public class ContextualService {
232
private final Service delegate;
233
234
public void serviceMethod(Context context) {
235
try (Scope scope = context.makeCurrent()) {
236
// Service method runs with provided context
237
delegate.performAction();
238
}
239
}
240
}
241
```