Non-Singleton Bean Injected Into Singleton
ID |
java.spring_non_singleton_bean_injected |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
best-practice, reliability, spring |
Description
Reports an injection point in a singleton Spring bean — an @Autowired field, a parameter of a constructor Spring autowires, or a parameter of an @Autowired method — whose injected type carries a @Scope other than "singleton".
Rationale
Singleton beans, and everything they depend on, are built once when the application context starts. If a singleton keeps a reference to a bean with a shorter lifecycle — "prototype", "request", "session" — it holds onto that first instance forever instead of getting a new one on every request, session or lookup. The code reads as correct, but state that should reset per request instead leaks across every request that follows.
@Component
@Scope("prototype")
public class CartCalculator {
private BigDecimal total = BigDecimal.ZERO;
// ...
}
@Service
public class CheckoutService {
@Autowired
private CartCalculator calculator; // Bad: one CartCalculator for the whole application
}
Remediation
Defer the lookup to call time instead of injecting the dependency directly: ObjectFactory<T>, ObjectProvider<T>, Provider<T> (JSR-330) or the ApplicationContext all resolve a fresh instance on every call.
@Service
public class CheckoutService {
private final ObjectFactory<CartCalculator> calculatorFactory;
@Autowired
public CheckoutService(ObjectFactory<CartCalculator> calculatorFactory) {
this.calculatorFactory = calculatorFactory;
}
public void checkout() {
CartCalculator calculator = calculatorFactory.getObject(); // Good: new instance per call
}
}