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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring Plugin: Build Dynamic Payment Strategies with Spring Boot 3

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring BootPayment StrategySpring Plugin
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.