Carrying data within class scope in Spring context

Viewed 76

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:

  1. 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.
  2. 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.
  3. 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?

0 Answers
Related