0

Is there a way to autowire an object that needs to be re-instantiated frequently?

I am using Netflix's DGS + spring boot framework, and basically storing the user authentication details in a custom context which is created for each request. I am trying to avoid adding context to the method signature because of the large amount of refactoring needed.

e.g.

public Result dataFetcher(DataFetchingEnvironment dfe) {
   

   // this context contains user details which is used for authorization
   // instantiated for every request
   setRolesInContext(dfe);
   MyCustomContext context = DgsContext.getCustomContext(dfe);

   // trying to avoid adding context as an extra param e.g. dataFetcherHelper(context)
   dataFetcherHelper(); // this calls other helper methods from other classes
}

I was thinking of using the facade pattern but this would not be thread safe. Basically autowire the RequestContextHolder, and call setRequestContext each time a new context gets initialized.

@Component
@NoArgsConstructor
@Getter
@Setter
public class RequestContextHolder {
    private RequestContext requestContext;
}
zeng
  • 23
  • 5

1 Answers1

0

I'm not sure how your question:

Is there a way to autowire an object that needs to be re-instantiated frequently?

Is related to the use case that you've presented in the question...

From the question it looks like you can consider using ThreadLocals as a conceptual "substitution" to the global variable available all over the place in the request if you don't want to add parameters to the methods to propagate the context through the flow.

This will work only in "thread-per-request" model, it won't work for reactive systems and for the complicated cases where you maintain different thread pools and switch the threads while implementing the Business Logic on backend:

So to achieve "thread-safety" in your context holder that you have suggested you can use:

@Configuration
public class MyConfig {
  @Bean
  public ThreadLocal<MyCustomContext> ctxHolder() {
     return new ThreadLocal<>();
  }
}

Then, again, if you're working in thread-per-request model, you can:

@Component
public class DataFetcherInterceptor {
   @Autowired
   private ThreadLocal<MyCustomContext> ctxHolder;

   public Result dataFetcher(DataFetchingEnvironment dfe) {
   

      // this context contains user details which is used for authorization
      // instantiated for every request
      setRolesInContext(dfe);
      MyCustomContext context = DgsContext.getCustomContext(dfe);
      ctxHolder.set(context);
      dataFetcherHelper(); 
   }
}

In the dataFetcherHelper or in general in any method that requires the access to the context you can:

public class SomeService {
   @Autowired ThreadLocal<MyCustomContext> ctxHolder;

   public void dataFetcherHelper() {
      MyCustomContext ctx = ctxHolder.get();
   }

Now, I see that dataFetcherHelper is just a method that you call from withing this "interceptor" class, in this case its an overkill, but I assume, you've intended that this is actually a method that belongs to another class, that might be an element in the call-chain of different classes. For these situations, this can be a working solution.

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • Thanks for the thoughtful answer Mark, that definitely gave me a good start. I ended up figuring out that Spring beans can be scoped to request level for web-aware applications, which means that each bean can have it's own instance per request. https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes – zeng Sep 01 '21 at 16:51