Detecting unused Spring beans

Viewed 4048

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?

1 Answers

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).

Related