javaspring-bootdependency-injection

Injecting a (user) context without Web/HttpContext


I have the following problem:

My environment is a typical Spring Boot application. I have Service-classes with business logic, which are using some metainformation, like "locales", "userId", "userId" and active modules or features. I used them by defining an own (Request-Scoped) Context-Object, which can be injected/autowired.

But now I have additional entry points, which does not come from an HTTP-Servlet-Request but from other technology like event based streams. Do you know, if there is a similar solution in parallel to a WebContext? I'm looking for a way to define such metadata in any Context, which can be injected by my Service classes without handover it into the deepest part of the code. Using Singletons seems not right, because each method-call/event can have an own Conext. So I'm looking for something like a "EventMessage"-Scope, similar to Request-Scope for HTTP-Request.

Hopefully you understand what I mean.


Solution

  • In the meantime I found a solution. Comparing to the WebApplicationContext of Spring, it is possible to create an own Context. This can be implemented by a ContextHolder:

    public class MyContextHolder {
    
    private static final ThreadLocal<MyContext> myContextThreadLocal = new NamedThreadLocal<>("My own Context");
    
    public static void registerContext(MyContext myContext) {
        myContextThreadLocal.set(myContext);
    }
    
    public static void clearContext() {
        myContextThreadLocal.remove();
    }
    
    public static MyContext determineContext() {
        return Optional.ofNullable(myContextThreadLocal.get())
            .orElseGet(() -> Optional.ofNullable(RequestContextHolder.currentRequestAttributes()) // Fallback using WebApplicationContext
                .filter(ServletRequestAttributes.class::isInstance)
                .map(ServletRequestAttributes.class::cast)
                .map(ServletRequestAttributes::getRequest)
                .map(MyContextHolder::determineFromHttpRequest) 
                .orElseThrow();
    }
    }
    

    Calling this Holder in a bean is returning the right Context, in case I am not in an Http-Thread. I am able to provide information outside of an HttpServletRequest. This seems to work.