0
# Extension System
1
2
Apache Dubbo's SPI (Service Provider Interface) extension system provides a powerful and flexible mechanism for customizing all major framework components. It enables plugin-style architecture where core functionality can be extended or replaced without modifying the core framework.
3
4
## Capabilities
5
6
### Extension Loading
7
8
The `ExtensionLoader` class provides the core mechanism for loading and managing extensions.
9
10
```java { .api }
11
/**
12
* Extension loader for SPI-based extension management
13
* @param <T> Extension interface type
14
*/
15
public class ExtensionLoader<T> {
16
/**
17
* Get extension loader for specific type
18
* @param type Extension interface class
19
* @return Extension loader instance
20
*/
21
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type);
22
23
/**
24
* Get extension instance by name
25
* @param name Extension name
26
* @return Extension instance
27
*/
28
public T getExtension(String name);
29
30
/**
31
* Get adaptive extension that can select implementation at runtime
32
* @return Adaptive extension instance
33
*/
34
public T getAdaptiveExtension();
35
36
/**
37
* Get default extension instance
38
* @return Default extension instance
39
*/
40
public T getDefaultExtension();
41
42
/**
43
* Get activate extensions based on URL parameters
44
* @param url Service URL with parameters
45
* @param values Parameter values for activation
46
* @param group Extension group
47
* @return List of activated extensions
48
*/
49
public List<T> getActivateExtensions(URL url, String[] values, String group);
50
51
/**
52
* Get all supported extension names
53
* @return Set of extension names
54
*/
55
public Set<String> getSupportedExtensions();
56
57
/**
58
* Check if extension exists
59
* @param name Extension name
60
* @return True if extension exists
61
*/
62
public boolean hasExtension(String name);
63
64
/**
65
* Add extension programmatically
66
* @param name Extension name
67
* @param clazz Extension class
68
*/
69
public void addExtension(String name, Class<?> clazz);
70
71
/**
72
* Remove extension
73
* @param name Extension name
74
*/
75
public void removeExtension(String name);
76
77
/**
78
* Get loaded extension names
79
* @return Set of loaded extension names
80
*/
81
public Set<String> getLoadedExtensions();
82
83
/**
84
* Get loaded extension instance
85
* @param name Extension name
86
* @return Extension instance if loaded
87
*/
88
public Object getLoadedExtension(String name);
89
}
90
```
91
92
**Usage Examples:**
93
94
```java
95
import org.apache.dubbo.common.extension.ExtensionLoader;
96
import org.apache.dubbo.rpc.Protocol;
97
98
// Get protocol extension loader
99
ExtensionLoader<Protocol> protocolLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
100
101
// Get specific protocol implementation
102
Protocol dubboProtocol = protocolLoader.getExtension("dubbo");
103
Protocol restProtocol = protocolLoader.getExtension("rest");
104
105
// Get adaptive protocol that selects implementation at runtime
106
Protocol adaptiveProtocol = protocolLoader.getAdaptiveExtension();
107
108
// List all available protocols
109
Set<String> supportedProtocols = protocolLoader.getSupportedExtensions();
110
System.out.println("Supported protocols: " + supportedProtocols);
111
```
112
113
### Extension Point Declaration
114
115
Extensions are declared using the `@SPI` annotation on interfaces.
116
117
```java { .api }
118
/**
119
* SPI extension point marker annotation
120
*/
121
@Target(ElementType.TYPE)
122
@Retention(RetentionPolicy.RUNTIME)
123
public @interface SPI {
124
/**
125
* Default extension name
126
* @return Default extension name
127
*/
128
String value() default "";
129
130
/**
131
* Extension scope for lifecycle management
132
* @return Extension scope
133
*/
134
ExtensionScope scope() default ExtensionScope.FRAMEWORK;
135
}
136
137
/**
138
* Extension scope enumeration
139
*/
140
public enum ExtensionScope {
141
FRAMEWORK, // Framework-level singleton
142
APPLICATION, // Application-level singleton
143
MODULE, // Module-level singleton
144
SELF // Instance-level, not singleton
145
}
146
```
147
148
**Usage Examples:**
149
150
```java
151
// Declare a custom extension point
152
@SPI("default")
153
public interface CustomLoadBalance {
154
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation);
155
}
156
157
// Implementation
158
public class MyLoadBalance implements CustomLoadBalance {
159
@Override
160
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
161
// Custom load balancing logic
162
return invokers.get(0);
163
}
164
}
165
```
166
167
### Adaptive Extensions
168
169
Adaptive extensions allow runtime selection of implementations based on URL parameters.
170
171
```java { .api }
172
/**
173
* Adaptive extension annotation for runtime selection
174
*/
175
@Target({ElementType.METHOD, ElementType.TYPE})
176
@Retention(RetentionPolicy.RUNTIME)
177
public @interface Adaptive {
178
/**
179
* Parameter keys for adaptive selection
180
* @return Parameter key names
181
*/
182
String[] value() default {};
183
}
184
```
185
186
**Usage Examples:**
187
188
```java
189
@SPI("dubbo")
190
public interface Protocol {
191
int getDefaultPort();
192
193
// Adaptive method selects implementation based on URL protocol
194
@Adaptive
195
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
196
197
// Adaptive method with custom parameter key
198
@Adaptive({"protocol"})
199
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
200
}
201
202
// Usage - the adaptive extension will select "dubbo" or "rest" protocol
203
// based on the URL protocol parameter
204
URL url = URL.valueOf("rest://localhost:8080/service");
205
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
206
Invoker<Service> invoker = protocol.refer(Service.class, url); // Uses REST protocol
207
```
208
209
### Extension Activation
210
211
The `@Activate` annotation enables conditional activation of extensions.
212
213
```java { .api }
214
/**
215
* Extension activation annotation for conditional loading
216
*/
217
@Target(ElementType.TYPE)
218
@Retention(RetentionPolicy.RUNTIME)
219
public @interface Activate {
220
/**
221
* Activation groups
222
* @return Group names
223
*/
224
String[] group() default {};
225
226
/**
227
* Activation parameter keys
228
* @return Parameter keys that trigger activation
229
*/
230
String[] value() default {};
231
232
/**
233
* Extensions that must be loaded before this one
234
* @return Extension names
235
*/
236
String[] before() default {};
237
238
/**
239
* Extensions that must be loaded after this one
240
* @return Extension names
241
*/
242
String[] after() default {};
243
244
/**
245
* Activation order (lower value = higher priority)
246
* @return Order value
247
*/
248
int order() default 0;
249
}
250
```
251
252
**Usage Examples:**
253
254
```java
255
// Filter that activates for consumer side only
256
@Activate(group = "consumer", order = -10000)
257
public class ConsumerContextFilter implements Filter {
258
@Override
259
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
260
// Consumer-side filtering logic
261
return invoker.invoke(invocation);
262
}
263
}
264
265
// Filter that activates when cache parameter is present
266
@Activate(group = {"consumer", "provider"}, value = "cache")
267
public class CacheFilter implements Filter {
268
@Override
269
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
270
// Caching logic
271
return invoker.invoke(invocation);
272
}
273
}
274
275
// Get activated filters
276
ExtensionLoader<Filter> filterLoader = ExtensionLoader.getExtensionLoader(Filter.class);
277
URL url = URL.valueOf("dubbo://localhost:20880/service?cache=lru");
278
List<Filter> filters = filterLoader.getActivateExtensions(url, new String[]{"cache"}, "consumer");
279
```
280
281
### Extension Configuration
282
283
Extensions are configured through configuration files in the `META-INF/dubbo/` directory.
284
285
**Configuration File Format:**
286
```
287
# META-INF/dubbo/org.apache.dubbo.rpc.Protocol
288
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
289
rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
290
grpc=org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol
291
```
292
293
**Directory Structure:**
294
```
295
META-INF/
296
├── dubbo/ # Dubbo-specific extensions
297
├── dubbo/internal/ # Internal extensions
298
└── services/ # Standard Java SPI
299
```
300
301
### Wrapper Extensions
302
303
Wrapper extensions provide AOP-like functionality for decorating other extensions.
304
305
```java { .api }
306
/**
307
* Wrapper extension example
308
*/
309
public class ProtocolFilterWrapper implements Protocol {
310
private final Protocol protocol;
311
312
public ProtocolFilterWrapper(Protocol protocol) {
313
this.protocol = protocol;
314
}
315
316
@Override
317
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
318
// Add pre-processing
319
return protocol.export(buildInvokerChain(invoker));
320
}
321
322
@Override
323
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
324
// Add post-processing
325
return buildInvokerChain(protocol.refer(type, url));
326
}
327
328
private <T> Invoker<T> buildInvokerChain(Invoker<T> invoker) {
329
// Build filter chain
330
return invoker;
331
}
332
}
333
```
334
335
### Common Extension Points
336
337
Apache Dubbo provides numerous built-in extension points:
338
339
```java { .api }
340
// Core extension interfaces
341
@SPI("dubbo")
342
public interface Protocol { /* ... */ }
343
344
@SPI("random")
345
public interface LoadBalance { /* ... */ }
346
347
@SPI("failover")
348
public interface Cluster { /* ... */ }
349
350
@SPI("zookeeper")
351
public interface RegistryFactory { /* ... */ }
352
353
@SPI("hessian2")
354
public interface Serialization { /* ... */ }
355
356
@SPI("netty")
357
public interface Transporter { /* ... */ }
358
359
@SPI("javassist")
360
public interface ProxyFactory { /* ... */ }
361
362
@SPI
363
public interface Filter { /* ... */ }
364
365
@SPI("jdk")
366
public interface Compiler { /* ... */ }
367
```
368
369
**Creating Custom Extensions:**
370
371
```java
372
// 1. Define extension interface
373
@SPI("default")
374
public interface CustomService {
375
String process(String input);
376
}
377
378
// 2. Implement extension
379
public class MyCustomService implements CustomService {
380
@Override
381
public String process(String input) {
382
return "Processed: " + input;
383
}
384
}
385
386
// 3. Create configuration file: META-INF/dubbo/com.example.CustomService
387
// my=com.example.MyCustomService
388
389
// 4. Use extension
390
ExtensionLoader<CustomService> loader = ExtensionLoader.getExtensionLoader(CustomService.class);
391
CustomService service = loader.getExtension("my");
392
String result = service.process("test");
393
```
394
395
### Extension Injection
396
397
Dubbo supports automatic injection of dependencies into extensions.
398
399
```java { .api }
400
/**
401
* Extension with dependency injection
402
*/
403
public class MyProtocol implements Protocol {
404
// Dubbo will automatically inject the LoadBalance extension
405
private LoadBalance loadBalance;
406
407
// Setter injection
408
public void setLoadBalance(LoadBalance loadBalance) {
409
this.loadBalance = loadBalance;
410
}
411
412
@Override
413
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
414
// Use injected loadBalance
415
return new MyInvoker<>(type, url, loadBalance);
416
}
417
}
418
```
419
420
### Extension Lifecycle
421
422
Extensions participate in Dubbo's lifecycle management.
423
424
```java { .api }
425
/**
426
* Lifecycle-aware extension
427
*/
428
public class MyExtension implements Protocol, Lifecycle {
429
private volatile boolean initialized = false;
430
431
@Override
432
public void initialize() throws IllegalStateException {
433
// Extension initialization logic
434
initialized = true;
435
}
436
437
@Override
438
public void start() throws IllegalStateException {
439
// Extension startup logic
440
}
441
442
@Override
443
public void destroy() throws IllegalStateException {
444
// Extension cleanup logic
445
initialized = false;
446
}
447
448
@Override
449
public boolean isInitialized() {
450
return initialized;
451
}
452
}
453
```
454
455
**Extension Best Practices:**
456
457
1. **Use meaningful names** for extensions in configuration files
458
2. **Implement proper error handling** in extension methods
459
3. **Follow single responsibility principle** for each extension
460
4. **Use @Activate wisely** to avoid unnecessary extension loading
461
5. **Provide clear documentation** for custom extensions
462
6. **Test extensions** in isolation and integration scenarios
463
7. **Consider performance impact** of wrapper extensions
464
8. **Use dependency injection** instead of direct extension loading