I'm writing a spring boot component/service that takes a composite structure, enriches it, and converts it to a different composite structure.
Made up code example:
@Component
public class Enricher {
private final ExternalCache cache;
public Result enrich(Request req) {
Result result = new Result();
result.setLayers(req.getAllLevels().forEach(level -> toLayer(level)));
return result;
}
private Layer toLayer(Level level) {
Layer layer = new Layer();
layer.setLevels(level.getAllSubLevels().forEach(subLevel -> toSubLayer(subLevel)));
return layer;
}
private SubLayer toSubLayer(SubLevel subLevel) {
// Need a property of the original request here!
SubLayer subLayer = new SubLayer(cache.get(requestId));
// ... Applying other logic to SubLayer here ...
return subLayer;
}
}
In order to create a SubLayer, I need a property of the request instance. Since every spring component is a singleton by definition, it shouldn't hold any request state. Therefore, if I want to use a property of the request, I have to do one of the following:
- Propagate it throughout the call stack - will work, but makes the code very complex and unreadable, especially if I need more than one property on the lower level.
- Use ThreadLocal, set it at the beginning of the enrich() method, and clear it prior to returning from it - more readable, but also error-prone and kind of misses the point of a ThreadLocal.
- Declare the bean as Prototype - doesn't help, because storing state is still problematic and this also might affect the caller bean (might have to turn it into a prototype as well).
Had it not been a spring bean, I could've just declared these parameters as fields and set them in the constructor, but I still want to enjoy the benefits of a spring bean.
Is there a way to get around this problem in an elegant manner?