0
# Providers & Scopes
1
2
Custom instance creation and lifecycle management including built-in scopes and provider interfaces for complex object construction.
3
4
## Capabilities
5
6
### Provider Interface
7
8
Provides instances of type T, used for custom object creation and lazy initialization.
9
10
```java { .api }
11
/**
12
* An object capable of providing instances of type T. Providers are used in numerous
13
* ways by Guice when the default means for obtaining instances is insufficient.
14
* @param <T> the type of object this provides
15
*/
16
public interface Provider<T> extends jakarta.inject.Provider<T> {
17
/**
18
* Provides an instance of T.
19
* @return instance of T
20
* @throws OutOfScopeException when an attempt is made to access a scoped object
21
* while the scope in question is not currently active
22
* @throws ProvisionException if an instance cannot be provided. Such exceptions include messages
23
* and throwables to describe why provision failed.
24
*/
25
@Override
26
T get();
27
}
28
```
29
30
**Usage Examples:**
31
32
```java
33
// Custom provider implementation
34
public class DatabaseConnectionProvider implements Provider<DatabaseConnection> {
35
private final String connectionUrl;
36
private final int maxRetries;
37
38
@Inject
39
public DatabaseConnectionProvider(
40
@Named("db.url") String connectionUrl,
41
@Named("db.max.retries") int maxRetries
42
) {
43
this.connectionUrl = connectionUrl;
44
this.maxRetries = maxRetries;
45
}
46
47
@Override
48
public DatabaseConnection get() {
49
for (int i = 0; i < maxRetries; i++) {
50
try {
51
return DriverManager.getConnection(connectionUrl);
52
} catch (SQLException e) {
53
if (i == maxRetries - 1) {
54
throw new RuntimeException("Failed to connect after " + maxRetries + " attempts", e);
55
}
56
try {
57
Thread.sleep(1000 * (i + 1)); // Exponential backoff
58
} catch (InterruptedException ie) {
59
Thread.currentThread().interrupt();
60
throw new RuntimeException("Interrupted while retrying connection", ie);
61
}
62
}
63
}
64
throw new RuntimeException("Should never reach here");
65
}
66
}
67
68
// Bind to provider
69
public class DatabaseModule extends AbstractModule {
70
@Override
71
protected void configure() {
72
bind(DatabaseConnection.class).toProvider(DatabaseConnectionProvider.class);
73
}
74
}
75
76
// Inject providers for lazy initialization
77
public class UserRepository {
78
private final Provider<DatabaseConnection> connectionProvider;
79
80
@Inject
81
public UserRepository(Provider<DatabaseConnection> connectionProvider) {
82
this.connectionProvider = connectionProvider;
83
}
84
85
public User findById(String id) {
86
// Connection created only when needed
87
DatabaseConnection conn = connectionProvider.get();
88
return conn.query("SELECT * FROM users WHERE id = ?", id);
89
}
90
}
91
```
92
93
### Scope Interface
94
95
Strategy for controlling instance lifecycle and reuse.
96
97
```java { .api }
98
/**
99
* A scope is a level of visibility that instances provided by Guice may have.
100
* By default, an instance created by the Injector has no scope, meaning it is
101
* created each time it is needed.
102
*/
103
public interface Scope {
104
/**
105
* Scopes a provider. The returned provider returns objects from this scope.
106
* If an object does not exist in this scope, the provider can use the given
107
* unscoped provider to retrieve one.
108
* @param key Key identifying the binding
109
* @param unscoped Provider to use when no scoped instance exists
110
* @return Scoped provider
111
*/
112
<T> Provider<T> scope(Key<T> key, Provider<T> unscoped);
113
114
/**
115
* Returns a string representation of this scope. Used for error messages.
116
* @return String representation
117
*/
118
String toString();
119
}
120
```
121
122
### Scopes Class
123
124
Built-in scope implementations.
125
126
```java { .api }
127
/**
128
* Built-in scope instances.
129
*/
130
public final class Scopes {
131
/**
132
* One instance per Injector. Also see @Singleton.
133
*/
134
public static final Scope SINGLETON = new SingletonScope();
135
136
/**
137
* No scoping; instances are created every time they're requested.
138
*/
139
public static final Scope NO_SCOPE = new NoScope();
140
141
/**
142
* Returns true if the given scope is a singleton scope.
143
* @param scope Scope to check
144
* @return true if singleton scope
145
*/
146
public static boolean isSingleton(Scope scope);
147
}
148
```
149
150
**Usage Examples:**
151
152
```java
153
// Using built-in scopes
154
public class ServiceModule extends AbstractModule {
155
@Override
156
protected void configure() {
157
// Singleton scope - one instance per injector
158
bind(DatabaseConnectionPool.class).in(Scopes.SINGLETON);
159
bind(Configuration.class).in(Singleton.class); // Equivalent
160
161
// No scope - new instance every time
162
bind(RequestHandler.class).in(Scopes.NO_SCOPE);
163
164
// Default is no scope, so this is equivalent:
165
bind(RequestHandler.class);
166
}
167
}
168
```
169
170
### Custom Scope Implementation
171
172
```java
173
// Custom scope example: Request scope for web applications
174
public class RequestScope implements Scope {
175
private final ThreadLocal<Map<Key<?>, Object>> requestScopedObjects =
176
new ThreadLocal<Map<Key<?>, Object>>();
177
178
public void enter() {
179
requestScopedObjects.set(new HashMap<Key<?>, Object>());
180
}
181
182
public void exit() {
183
requestScopedObjects.remove();
184
}
185
186
@Override
187
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
188
return () -> {
189
Map<Key<?>, Object> scopedObjects = requestScopedObjects.get();
190
if (scopedObjects == null) {
191
throw new OutOfScopeException("Cannot access " + key + " outside of request scope");
192
}
193
194
@SuppressWarnings("unchecked")
195
T current = (T) scopedObjects.get(key);
196
if (current == null) {
197
current = unscoped.get();
198
scopedObjects.put(key, current);
199
}
200
return current;
201
};
202
}
203
204
@Override
205
public String toString() {
206
return "RequestScope";
207
}
208
}
209
210
// Custom scope annotation
211
@ScopeAnnotation
212
@Target({TYPE, METHOD})
213
@Retention(RUNTIME)
214
public @interface RequestScoped {}
215
216
// Register custom scope
217
public class WebModule extends AbstractModule {
218
private final RequestScope requestScope = new RequestScope();
219
220
@Override
221
protected void configure() {
222
bindScope(RequestScoped.class, requestScope);
223
224
// Bind request-scoped objects
225
bind(UserSession.class).in(RequestScoped.class);
226
bind(RequestContext.class).in(RequestScoped.class);
227
}
228
229
@Provides
230
@Exposed
231
RequestScope provideRequestScope() {
232
return requestScope;
233
}
234
}
235
236
// Use custom scope
237
@RequestScoped
238
public class UserSession {
239
private String userId;
240
private long startTime;
241
242
@Inject
243
public UserSession() {
244
this.startTime = System.currentTimeMillis();
245
}
246
247
// Session methods
248
}
249
```
250
251
## Provider Utilities
252
253
### Providers Class
254
255
Static utilities for creating Provider instances.
256
257
```java { .api }
258
/**
259
* Static utility methods for creating and working with Provider instances.
260
*/
261
public final class Providers {
262
/**
263
* Returns a provider that always provides the same instance.
264
* @param instance Instance to provide
265
* @return Provider that returns the instance
266
*/
267
public static <T> Provider<T> of(T instance);
268
269
/**
270
* Converts a JSR-330 Provider to a Guice Provider.
271
* @param provider JSR-330 provider
272
* @return Guice provider
273
*/
274
public static <T> Provider<T> guicify(javax.inject.Provider<T> provider);
275
}
276
```
277
278
**Usage Examples:**
279
280
```java
281
// Create providers for constant values
282
Provider<String> appNameProvider = Providers.of("MyApplication");
283
Provider<Integer> versionProvider = Providers.of(42);
284
285
// Use in bindings
286
public class ConstantModule extends AbstractModule {
287
@Override
288
protected void configure() {
289
bind(String.class).annotatedWith(Names.named("app.name"))
290
.toProvider(Providers.of("MyApplication"));
291
bind(Integer.class).annotatedWith(Names.named("app.version"))
292
.toProvider(Providers.of(2));
293
}
294
}
295
296
// Convert JSR-330 providers
297
javax.inject.Provider<DatabaseService> jsr330Provider = () -> new DatabaseServiceImpl();
298
Provider<DatabaseService> guiceProvider = Providers.guicify(jsr330Provider);
299
```
300
301
## Advanced Provider Patterns
302
303
### Conditional Providers
304
305
```java
306
public class ConditionalDatabaseProvider implements Provider<DatabaseService> {
307
private final String environment;
308
private final Provider<ProductionDatabase> prodProvider;
309
private final Provider<TestDatabase> testProvider;
310
311
@Inject
312
public ConditionalDatabaseProvider(
313
@Named("environment") String environment,
314
Provider<ProductionDatabase> prodProvider,
315
Provider<TestDatabase> testProvider
316
) {
317
this.environment = environment;
318
this.prodProvider = prodProvider;
319
this.testProvider = testProvider;
320
}
321
322
@Override
323
public DatabaseService get() {
324
if ("production".equals(environment)) {
325
return prodProvider.get();
326
} else {
327
return testProvider.get();
328
}
329
}
330
}
331
```
332
333
### Caching Providers
334
335
```java
336
public class CachingProvider<T> implements Provider<T> {
337
private final Provider<T> delegate;
338
private volatile T instance;
339
340
public CachingProvider(Provider<T> delegate) {
341
this.delegate = delegate;
342
}
343
344
@Override
345
public T get() {
346
if (instance == null) {
347
synchronized (this) {
348
if (instance == null) {
349
instance = delegate.get();
350
}
351
}
352
}
353
return instance;
354
}
355
}
356
```
357
358
### Resource Management Providers
359
360
```java
361
public class ManagedResourceProvider implements Provider<DatabaseConnection> {
362
private final String connectionUrl;
363
private final Set<DatabaseConnection> activeConnections = new ConcurrentHashMap<>();
364
365
@Inject
366
public ManagedResourceProvider(@Named("db.url") String connectionUrl) {
367
this.connectionUrl = connectionUrl;
368
369
// Register shutdown hook to clean up connections
370
Runtime.getRuntime().addShutdownHook(new Thread(this::cleanup));
371
}
372
373
@Override
374
public DatabaseConnection get() {
375
try {
376
DatabaseConnection conn = DriverManager.getConnection(connectionUrl);
377
activeConnections.add(conn);
378
return conn;
379
} catch (SQLException e) {
380
throw new RuntimeException("Failed to create database connection", e);
381
}
382
}
383
384
private void cleanup() {
385
for (DatabaseConnection conn : activeConnections) {
386
try {
387
conn.close();
388
} catch (SQLException e) {
389
// Log error but continue cleanup
390
}
391
}
392
activeConnections.clear();
393
}
394
}
395
```
396
397
## Exception Types
398
399
### OutOfScopeException
400
401
Thrown when accessing scoped objects outside their scope.
402
403
```java { .api }
404
/**
405
* Thrown when an attempt is made to access a scoped object when the scope
406
* in question is not currently active.
407
*/
408
public class OutOfScopeException extends RuntimeException {
409
/**
410
* Creates a new OutOfScopeException.
411
* @param message Error message
412
*/
413
public OutOfScopeException(String message);
414
415
/**
416
* Creates a new OutOfScopeException.
417
* @param message Error message
418
* @param cause Underlying cause
419
*/
420
public OutOfScopeException(String message, Throwable cause);
421
}