Using global modelattribute in another controller advice in spring

3 min read 05-10-2024
Using global modelattribute in another controller advice in spring


Accessing Global ModelAttributes in Another Controller Advice in Spring

Problem: You're working on a Spring application and want to access data stored in a global Model object from one controller advice in another controller advice. This can be useful for things like setting up global application state, user authentication, or logging information across the application.

Rephrased: Imagine you have a central storage space in your Spring application where you put information that needs to be available everywhere. You want to be able to access that storage from different parts of the application. This article will guide you on how to access this global storage using controller advice in Spring.

Scenario & Code:

Consider the following scenario:

  • GlobalControllerAdvice.java:
@ControllerAdvice
public class GlobalControllerAdvice {

    @ModelAttribute
    public void setGlobalAttribute(Model model) {
        model.addAttribute("globalKey", "Global Value");
    }
}
  • AnotherControllerAdvice.java:
@ControllerAdvice
public class AnotherControllerAdvice {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleException(Exception ex, Model model) {
        // Want to access "globalKey" from the model here
        String globalValue = model.getAttribute("globalKey", String.class);

        return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

In this example, GlobalControllerAdvice sets a global attribute "globalKey" with a value "Global Value". We want to access this value in the exception handler in AnotherControllerAdvice. However, directly accessing model.getAttribute("globalKey") in AnotherControllerAdvice will return null because model is a new instance created for each request.

Solution & Insights:

To access global attributes in another controller advice, we need a shared storage mechanism. The easiest way is to use a singleton bean like ApplicationContext. Here's how:

  1. Inject ApplicationContext: Inject the ApplicationContext into your AnotherControllerAdvice:

    @ControllerAdvice
    public class AnotherControllerAdvice {
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @ExceptionHandler(Exception.class)
        public ResponseEntity<String> handleException(Exception ex, Model model) {
            // Access global attribute from ApplicationContext
            String globalValue = applicationContext.getBean("globalKey", String.class);
    
            return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
  2. Store and Retrieve Global Attributes: The applicationContext provides access to the entire Spring application context, allowing you to retrieve global attributes stored within it.

    • In your GlobalControllerAdvice, instead of adding the attribute directly to the Model, store it in the ApplicationContext:

      @ControllerAdvice
      public class GlobalControllerAdvice {
      
          @Autowired
          private ApplicationContext applicationContext;
      
          @ModelAttribute
          public void setGlobalAttribute() {
              applicationContext.getBeanFactory().registerSingleton("globalKey", "Global Value");
          }
      }
      
    • In your AnotherControllerAdvice, retrieve the global attribute using applicationContext.getBean():

      @ExceptionHandler(Exception.class)
      public ResponseEntity<String> handleException(Exception ex, Model model) {
          String globalValue = applicationContext.getBean("globalKey", String.class);
      
          return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
      }
      

Key Points:

  • Model Scope: Remember that Model objects in Spring MVC are request-scoped, meaning each request gets a new instance.
  • ApplicationContext Scope: The ApplicationContext is a singleton, allowing you to store and retrieve global attributes across requests.
  • Alternatives: You can also consider using Spring's Session or Application scope for storing global data depending on your specific needs.

Benefits:

  • Centralized data management: Global attributes allow you to share information across different parts of your application.
  • Code Reusability: By accessing global attributes from controller advice, you can easily reuse the same information in multiple exception handlers or other aspects of your application.
  • Simplified logic: You don't need to pass data around between controllers or services, as you can directly access it from the global context.

Additional Information:

Conclusion:

By utilizing the ApplicationContext, you can seamlessly access global attributes from different controller advice classes in your Spring application, simplifying your code and ensuring consistency in your application logic.