Library for building Chain of Responsibility in Spring applications without manual wiring of dependencies.
Automatically links beans into a chain using @Order and @ChainNext.
sequenceDiagram
participant Client
participant ChainA
participant ChainB
participant ChainC
Client->>ChainA: execute()
ChainA->>ChainB: next.handle()
ChainB->>ChainC: next.handle()
- Automatic chain building via Spring context
- Supports
@Orderfor execution order - No manual wiring of chain dependencies
- Fully compatible with Spring AOP (proxies supported)
- Last class of chain contains Proxy implementation on chain interface when the class has field with
@ChainNext
- All beans implementing the chain interface are collected from the Spring context
- Beans are sorted using
@Order - The next element is injected via
@ChainNext
The common way to build an ordered chain in plain Spring, without this library, looks like this:
@Component
public class ManualChain {
private final List<ChainInterface> handlers;
public ManualChain(List<ChainInterface> handlers) {
this.handlers = new ArrayList<>(handlers);
this.handlers.sort(AnnotationAwareOrderComparator.INSTANCE);
}
public void handle() {
for (ChainInterface handler : handlers) {
handler.handle();
}
}
}That works for handlers that always run start to finish, but every handler has to know about the
whole list, and there's no clean way for a handler to stop the chain or skip the rest based on its
own logic. spring-chain-of-responsibility wires each bean directly to the next one via
@ChainNext, so a handler decides on its own whether to continue — a real Chain of Responsibility,
not just an ordered loop.
- Java 17+
- Spring Boot 3.x+ for automatic autoconfiguration via
AutoConfiguration.imports(see Without Spring Boot otherwise) - Built and tested against Spring Framework 7.0.x (via
spring-framework-bom)
<dependency>
<groupId>io.github.evmetatron</groupId>
<artifactId>spring-chain-of-responsibility</artifactId>
<version>0.2.0</version>
</dependency>dependencies {
implementation("io.github.evmetatron:spring-chain-of-responsibility:0.2.0")
}ChainFactory is registered automatically via Spring Boot autoconfiguration — just autowire it.
import io.github.evmetatron.spring.cor.ChainFactory;
import org.springframework.context.annotation.Bean;
@Bean
public ChainInterface chain(@Autowired ChainFactory chainFactory) {
return chainFactory.createChain(ChainInterface.class);
}The autoconfiguration file only gets picked up by Spring Boot's autoconfiguration mechanism.
In a plain Spring application, register ChainFactory yourself — either by importing the
provided configuration:
import io.github.evmetatron.spring.cor.ChainAutoConfiguration;
import org.springframework.context.annotation.Import;
@Import(ChainAutoConfiguration.class)
class AppConfig {}or by declaring the bean directly:
import io.github.evmetatron.spring.cor.ChainFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@Bean
public ChainFactory chainFactory(ApplicationContext context) {
return new ChainFactory(context);
}Injects the next element in the chain.
The order is defined using @Order.
- Do not forget
@Componentor@Service @Orderdefines execution order- Every bean must have a field annotated with
@ChainNext— otherwiseChainFactorythrowsChainNextFieldNotFoundException
import io.github.evmetatron.spring.cor.ChainNext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
public interface ChainInterface {
void handle();
}
@Component // or @Service
@Order(1)
public class ChainA implements ChainInterface {
@ChainNext
private ChainInterface next;
@Override
public void handle() {
/* Some code */
System.out.println("Chain A");
if (shouldContinue()) {
next.handle();
}
}
}
@Component
@Order(2)
public class ChainB implements ChainInterface {
@ChainNext
private ChainInterface next;
@Override
public void handle() {
/* Some code */
System.out.println("Chain B");
if (shouldContinue()) {
next.handle();
}
}
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ExampleClass {
@Autowired
private ChainInterface chain;
public void execute() {
chain.handle(); // call all chains
// Result:
// Chain A
// Chain B
}
}