Detecting unused Spring beans
Asked Answered
F

1

22

Given a Spring configuration that exclusively contains eager (non-lazy) singleton beans, i.e. the defaults, is it possible to have Spring throw an exception in the case where any of those beans is not injected anywhere? I'm essentially looking for a way to detect dead code in the form of Spring beans.

My question is somewhat similar to these.

However,

  • I'm not interested in manually inspecting a graph or parsing log data.
  • I don't have the added complexity of multiple context files, overriding beans, bean post-processing, or xml. It's a simple, straightforward, annotation-driven configuration.
  • I'm using Spring Boot 1.2.6 which is several years newer than those questions (maybe new functionality exists).

Spring will certainly throw an exception if a necessary bean is missing. Can it also throw an exception in the opposite scenario where a bean is found but unnecessary?

Frerichs answered 16/3, 2016 at 22:51 Comment(4)
Two ways to detect them "quickly", but it's a bit dirty : 1) Use visual VM from visualvm.github.io, make run your application and see the number of instances of all of your beans. 2) Declare private all of your beans (temporary) to check who is really calling them (the caller will be in error too). Re-put your beans classes public one by one and only when a caller is whining. The ones that are still private at the end are the one unused.Domenech
Also in IDEA you can press Ctrl+left mice click and it'll show you all usages of your bean (class)Northwestward
Note to self: another reason not to use Spring or anything that relies on reflection. Stick to the java compiler.Periodical
@SridharSarnobat, you may be interested in Micronaut as an alternative.Frerichs
A
4

Spring will certainly throw an exception if a necessary bean is missing. Can it also throw an exception in the opposite scenario where a bean is found but unnecessary?

TL/DR:

Spring does not support this (and probably never will).

Long version:

Detecting if a bean is used can be really hard.

First, lets define when does spring throw the "missing bean" exception.

During the initialisation of the spring context, spring creates the beans in the order in which it will allow for all dependencies to be satisfied (if possible). If a bean is missing a dependency, spring will throw an exception (as you said). So, the exception is thrown during the spring context initialisation process.

Now, you could say that we could monitor this process and look for a bean that was not used as a dependency in any other bean. The problem is that not all bean dependencies are defined during the spring context initialisation process.

Let's look at the following example:

First, we have a simple interface, DataService

public interface DataService {
 String getData();
}

Now we have 2 spring beans that implement this interface:

@Service("firstDataService")
public class FirstDataService implements DataService {

  @Override
  public String getData() {
    return "FIRST DATA SERVICE";
  }
}

@Service("secondDataService")
public class SecondDataService implements DataService {
  @Override
  public String getData() {
    return "SECOND DATA SERVICE";
  }
}

Now, imagine that there is no bean that depends on these two beans directly. When I say directly, I mean there is no bean that depends on these beans via constructor-based, setter-based or field-based dependency injection.

Because of that, spring will not inject these beans inside any other bean during the context initialisation process.

Now, consider the following bean:

@Service
public class DataCollector {

  @Autowired
  ApplicationContext applicationContext;


  String getDataFromService(String beanName) {
    DataService ds = (DataService) applicationContext.getBean(beanName);
    return ds.getData();
  }
}

If I call the getDataFromService method of the DataCollector bean with "firstDataService" value for the beanName parameter, the method will return "FIRST DATA SERVICE" as a result. If I call the method with "secondDataService", I will return "SECOND DATA SERVICE" as a result.

Now, when spring looks at the definition of DataController during context initialisation, there is no way to determine on which beans DataCollector depends on. It all depends on the application logic, and the value that we use for the beanName parameter when we call the getDataFromService method.

Because of that, spring is not capable of determining if there is bean that is never used (because the bean usage can be dynamic, like in the case above).

Aureus answered 26/12, 2018 at 16:34 Comment(2)
To be more clear about my setup, I tried to define it as the simplest, most common scenario. I was looking to detect when any of those beans is not injected: the keyword being injected. I understand the Service Locator anti-pattern, as shown in the last code example, is a different beast. A solution for pure dependency injection would be sufficient for my setup.Frerichs
@Frerichs Sorry for the late response. I understand what you mean but as far as I know there is no such feature in spring at the moment. I believe that such feature will probably confuse the users and cause them to use it in a wrong way. Anyway if you are interested in implementing something like that, you should take a look at the DepedencyDescriptor class which manages DI of the beans (here you can see which beans are being injected) and the DefaultListableBeanFactory which contains the beans that were created in the container.Aureus

© 2022 - 2024 — McMap. All rights reserved.