Mastering Spring Plugin: Build Dynamic Payment Strategies with Spring Boot 3
This article demonstrates how to use Spring Plugin in Spring Boot 3 to create a modular, extensible payment system supporting multiple methods, covering dependency setup, interface definitions, implementations, plugin registration, metadata handling, ordering, and testing, with complete code examples and diagrams.
Introduction
Building scalable architecture is a core principle for maintainable applications. While full‑featured plugin environments like OSGi are popular, they introduce considerable complexity. Spring Plugin offers a lightweight way to extend core system functionality without the heavy runtime features of OSGi.
Spring Plugin integrates tightly with Spring’s component model and adds custom extensions to the core container.
Practical Example
Scenario
A payment system must support multiple payment methods (WeChat, Alipay, Card). We need a way to switch between these methods dynamically based on runtime conditions.
2.1 Define Payment Interface
public interface PaymentProcessingService extends Plugin<PaymentMethod> {
String processPayment(BigDecimal amount);
}Plugin Core Interface
public interface Plugin<S> {
// Only defines a supports method to decide if the current implementation supports the given delimiter
boolean supports(S delimiter);
}Payment Method Enum
public enum PaymentMethod {
WX, ALIPAY, CARD
}2.2 Payment Implementations
WeChat (WX) Implementation
@Component
public class WXProcessingService implements PaymentProcessingService {
@Override
public String processPayment(BigDecimal amount) {
return String.format("微信支付完成: %s", amount);
}
@Override
public boolean supports(PaymentMethod delimiter) {
return delimiter.equals(PaymentMethod.WX);
}
}Other implementations follow the same pattern, only the supports method differs:
public boolean supports(PaymentMethod delimiter) {
return delimiter.equals(PaymentMethod.ALIPAY);
}
public boolean supports(PaymentMethod delimiter) {
return delimiter.equals(PaymentMethod.CARD);
}2.3 Configure Plugins
@Configuration
@EnablePluginRegistries(PaymentProcessingService.class)
public class PaymentPluginConfig {
}The annotation registers a FactoryBean<T> where T is a PluginRegistry. The registry can be injected directly.
Global Exception Handling
@RestControllerAdvice
public class GlobalExceptionAdvice {
@ExceptionHandler(NoSupportPaymentException.class)
public ResponseEntity<Object> handlePaymentException(NoSupportPaymentException e) {
return ResponseEntity.ok(e.getMessage());
}
}2.4 Testing
Inject the registry and use it to obtain the appropriate implementation:
@RestController
@RequestMapping("/payment")
public class PaymentController {
private final PluginRegistry<PaymentProcessingService, PaymentMethod> pluginRegistry;
public PaymentController(PluginRegistry<PaymentProcessingService, PaymentMethod> pluginRegistry) {
this.pluginRegistry = pluginRegistry;
}
@PostMapping
public ResponseEntity<String> pay(@RequestBody PaymentRequest paymentRequest) {
PaymentProcessingService service = pluginRegistry.getPluginFor(paymentRequest.getPaymentMethod())
.orElseThrow(() -> new NoSupportPaymentException("不支持的支付方式"));
return ResponseEntity.ok(service.processPayment(paymentRequest.getAmount()));
}
}Removing the @Component annotation from the Card implementation makes the system reject that method, as shown in the test screenshots.
Relation to Spring MVC
The pattern mirrors Spring MVC’s HandlerAdapter mechanism, where the supports method determines if a handler can process a request.
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}Other Features
3.1 Control Order
Use @Order or implement Ordered to set priority. Lower values have higher precedence.
@Component
@Order(1)
public class WXProcessingService implements PaymentProcessingService {}
@Component
@Order(0)
public class WX2ProcessingService implements PaymentProcessingService {}In this case, WX2ProcessingService will be selected.
3.2 Plugin Metadata
Two core interfaces drive metadata support:
public interface PluginMetadata {
String getName();
String getVersion();
}
public interface MetadataProvider {
PluginMetadata getMetadata();
}Extend the payment interface to also implement MetadataProvider:
public interface PaymentProcessingService extends Plugin<PaymentMethod>, MetadataProvider {
String processPayment(BigDecimal amount);
@Override
default PluginMetadata getMetadata() { return null; }
}Concrete implementations can override getMetadata to provide name and version:
@Component
@Order(1)
public class WXProcessingService implements PaymentProcessingService {
@Override
public PluginMetadata getMetadata() {
return new PluginMetadata() {
public String getVersion() { return "1.0.0"; }
public String getName() { return "微信支付"; }
};
}
}Abstract Metadata‑Based Plugin
Spring Plugin also offers an abstract base class that uses metadata for the supports check:
public abstract class AbstractMetadataBasedPlugin implements Plugin<PluginMetadata>, MetadataProvider {
public boolean supports(PluginMetadata delimiter) {
return getMetadata().equals(delimiter);
}
}These features together enable a clean, modular, and easily extensible payment architecture in Spring Boot 3.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
