04 — Getting Started

Dependency Injection in Rules

One of the biggest benefits of rulii-spring is that your @Rule classes participate in Spring's dependency injection. You can constructor-inject services, repositories, or any other Spring bean directly into your rules.

SpringObjectFactory

The magic happens in SpringObjectFactory. It extends rulii's DefaultObjectFactory and delegates instance creation to Spring's AutowireCapableBeanFactory.

When the rule engine needs to instantiate a rule class, it calls:

ctx.createBean(ruleClass)

This means every constructor parameter is resolved by Spring, just like any other Spring-managed bean.

Constructor Injection

The recommended pattern is constructor injection. Define your dependencies as constructor parameters and Spring will wire them:

@Rule
public class CreditCheckRule {

    private final CreditService creditService;
    private final AuditLogger auditLogger;

    @Autowired
    public CreditCheckRule(CreditService creditService, AuditLogger auditLogger) {
        this.creditService = creditService;
        this.auditLogger = auditLogger;
    }

    @Given
    public boolean hasSufficientCredit(String customerId, BigDecimal amount) {
        return creditService.getAvailableCredit(customerId)
            .compareTo(amount) >= 0;
    }

    @Then
    public void approve(String customerId, BigDecimal amount) {
        creditService.reserve(customerId, amount);
        auditLogger.log("Credit approved for " + customerId);
    }

    @Otherwise
    public void decline(String customerId) {
        auditLogger.log("Credit declined for " + customerId);
    }
}

Both CreditService and AuditLogger are resolved from the Spring context when the rule is created.

Lifecycle Management

SpringObjectFactory listens for the Spring context's ContextClosedEvent. When the application shuts down, the factory releases its reference to the AutowireCapableBeanFactory, preventing memory leaks. Any attempt to create a rule after shutdown throws a clear exception.

@EventListener
void handleContextRefreshEvent(ContextClosedEvent ctxClosedEvent) {
    // Releases reference to prevent stale context usage
    this.ctx = null;
}

What You Can Inject

Since rule creation is delegated entirely to Spring's createBean, you can inject anything Spring knows about:

  • Your own @Service and @Repository beans
  • Spring Data repositories
  • Environment, ApplicationContext, and other Spring infrastructure beans
  • Third-party beans (e.g., RestTemplate, WebClient)
  • @Value-annotated configuration properties

Tip

Prefer constructor injection over field injection. It makes your rules easier to test — just pass mocks through the constructor.