Implementing the Strategy Pattern with Spring Plugin

The Spring Plugin library provides a core interface for building plugin-based architectures:

package org.springframework.plugin.core;

public interface Plugin<S> {
    boolean supports(S criteria);
}

Here is an implementation of this interface for a payment processing scenario. The PayByPaypal class defines the logic for PayPal transactions:

import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class PayByPaypal implements PaymentPlugin {

    @Override
    public void pay(int amount) {
        log.info("Processing payment via PayPal. Total: " + amount);
    }

    @Override
    public boolean supports(PaymentMethod method) {
        return method == PaymentMethod.PAYPAL;
    }
}

The supports method acts as a selector. It determines if the specific plugin instance is applicable for the given context (in this case, the PaymentMethod). Similarly, a PayByCard class can be created to handle credit card logic.

To manage these plugins, a service class is required to register them and delegate the execution. We use the PluginRegistry provided by Spring.

package com.rkdevblog.plugin;

import java.util.Optional;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.plugin.core.config.EnablePluginRegistries;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@EnablePluginRegistries(PaymentPlugin.class)
public class PaymentService {

    private final PluginRegistry<PaymentPlugin, PaymentMethod> pluginRegistry;

    public PaymentPlugin resolvePlugin(PaymentMethod method) {
        Optional<PaymentPlugin> selectedPlugin = pluginRegistry.getPluginFor(method);
        return selectedPlugin.orElseThrow(() -> 
            new UnsupportedOperationException("Payment method not supported: " + method)
        );
    }
}

The @EnablePluginRegistries annotation creates a registry for the PaymentPlugin type. The PluginRegistry is then injected, allowing the service to look up the correct plugin insatnce based on the PaymentMethod enum.

To verify the behavior, you can initialize the flow in a runner bean. Below is an example testing the Card payment method:

@Bean
ApplicationRunner cardRunner(PaymentService paymentService) {
    return args -> {
        PaymentPlugin plugin = paymentService.resolvePlugin(PaymentMethod.CARD);
        plugin.pay(10);
    };
}

Similarly, testing the PayPal method:

@Bean
ApplicationRunner paypalRunner(PaymentService paymentService) {
    return args -> {
        PaymentPlugin plugin = paymentService.resolvePlugin(PaymentMethod.PAYPAL);
        plugin.pay(10);
    };
}

Tags: Spring Strategy Pattern Spring Plugin java Design Patterns

Posted on Thu, 04 Jun 2026 18:05:00 +0000 by Pointybeard