IntelliJ IDEA Java

Spring Debugger: ApplicationContext at Your Fingertips

IntelliJ IDEA has great support for debugging Java and Kotlin applications. But if you are building Spring Boot applications, you may want to access Spring components along with their metadata. That’s where the Spring Debugger plugin comes in. It makes life easier by letting you explore and use the entire Spring ApplicationContext right from your debugger.

Let’s dive into how this powerful plugin can revolutionize your debugging workflow.

Spring Debugger: ApplicationContext at Your Fingertips

Beyond the Scope: Access Any Spring Bean

Imagine you are building a bookmarks management application using Spring Boot and you have a Spring bean BookmarkService with createBookmark() method as follows:

@Service
public class BookmarkService {
   private final BookmarkRepository bookmarkRepository;
   private final CategoryService categoryService;

   public BookmarkService(BookmarkRepository bookmarkRepository,
                          CategoryService categoryService) {
       this.bookmarkRepository = bookmarkRepository;
       this.categoryService = categoryService;
   }

   @Transactional
   public Bookmark createBookmark(CreateBookmarkCmd cmd) {
       var bookmark = new Bookmark(cmd.title(), cmd.url());
       if(cmd.categoryName() != null) {
           Category category = categoryService.findByName(cmd.categoryName()).orElse(null);
           if (category == null) {
               category = categoryService.createCategory(new Category(cmd.categoryName()));
           }
           bookmark.setCategory(category);
       }
       bookmarkRepository.save(bookmark);
       return bookmark;
   }
}

The BookmarkService has BookmarkRepository and CategoryService beans injected. While creating a bookmark, we first check if there is a Category exists with the given name. If not, we will create a new Category and then save Bookmark.

The CategoryService is also a Spring bean with findByName(String) method as follows:

@Service
public class CategoryService {
   private final CategoryRepository categoryRepository;

   public CategoryService(CategoryRepository categoryRepository) {
       this.categoryRepository = categoryRepository;
   }

   @Cacheable("category-by-name")
   public Optional<Category> findByName(String name) {
       return categoryRepository.findByNameEqualsIgnoreCase(name);
   }
}

The findByName() method is annotated with @Cacheable("category-by-name") to cache the method result so that if you call this method with the same input it will return the cached result instead of executing the method logic again.

Now, Imagine you’re debugging create bookmark use case. You hit a breakpoint in BookmarkService.createBookmark() method. Usually, you can access objects within the current scope, such as bookmarkRepository, bookmark, or categoryService

But what if you need to inspect or interact with a bean that isn’t directly injected into your current class. For example, you may want to clear existing data or insert new data into the database by using a repository directly.

Let’s say while debugging BookmarkService.createBookmark() method, we would like to temporarily delete all the categories using CategoryRepository.deleteAll()? Usually, you’d be out of luck.

This is where the Spring Debugger plugin shines. It allows you to access any Spring bean that exists within your ApplicationContext. Simply start typing the bean name into the expression input box, and the plugin will show matching beans. You can then invoke any methods on them, like calling categoryRepository.findAll() method and you can view the result. 

This capability extends your debugging reach far beyond the immediate scope.

Manipulate Application State On-the-Fly

Consider a common scenario: you have a service method, like CategoryService.findByName(), that caches its results. If you’re debugging an issue related to this method and call it repeatedly with the same input, the breakpoint won’t be hit because the results are served from the cache.

Traditionally, you might need to restart your application or temporarily disable caching to force the breakpoint to hit. With the Spring Debugger plugin, there’s a much more elegant solution.

You can directly access the cacheManager bean from your ApplicationContext. Inspect the available caches, find the specific cache (e.g., category-by-name), and then invoke the invalidate() method on it.

After invalidating the cache, when you resume execution, the breakpoint in CategoryService will now be hit because there’s no entry in the cache for that name.

This demonstrates the incredible power to invoke any method on any Spring bean to dynamically control and debug your application’s flow.

Access Core Spring Components and Properties

Sometimes we may get tricky problems due to configuration issues or misunderstanding of conventions. While debugging such issues, we may want to explore the application’s runtime configuration or access low-level components to interact with database or publish application events using ApplicationEventPublisher.

The utility of the Spring Debugger plugin isn’t limited to your custom beans. It also provides direct access to core Spring components that are part of your ApplicationContext:

  • EntityManager: If you’re using JPA, you can directly access the entityManager and invoke any methods on it.
  • Environment: Gain access to the environment bean, which provides an abstraction to all application properties. You can query specific property values to see their current configuration.

Streamline Your Debugging Workflow and Save Time

The ability to access any Spring bean has significant implications for your overall debugging process.

  • Eliminate Temporary Code Changes: How many times have you temporarily injected a bean, added log statements, or introduced other debugging code, only to have to remove it once the issue is found? With Spring Debugger, you don’t need to make those temporary changes. You can directly access any bean and explore your use case as needed.
  • Effortless Test Data Setup: Imagine debugging a scenario that requires specific test data, perhaps an action only an administrator can perform. Instead of logging out, and logging in as an administrator, performing the action, and then returning to your debug flow, you can simply access a service bean directly to insert test data into the database, bypassing complex setup steps.

In essence, the Spring Debugger plugin empowers you with all the power of the Spring ApplicationContext right at your fingertips while debugging. It significantly enhances your ability to understand, inspect, and manipulate your Spring Boot application’s state, making your debugging sessions more efficient and productive.

Go ahead and install the Spring Debugger plugin to explore its powers yourself!

image description