Skip to content
Advertisement

Prevent injection of bean with narrower scope using Spring

I’m working on a Spring application using beans of different scopes. Many beans are singletons, other request or custom scoped. Especially using those custom scopes makes it sometimes difficult to find out which scope can be safely injected into which other scope or when e.g. a Provider<T> needs to be used.

I am aware that I can just create scope proxies for all beans that are basically not singletons, but in many cases that does not seem to be necessary. For example, a bean might only be supposed to be injected into other beans of the same scope, but not everyone working on the project might be aware of that. Thus, it would be great if one could somehow prevent “misuse” of those beans, especially if one might not always recognize the mistake in time.

So my question is: Is there some way to define which scoped can be safely injected into which scope and then prevent beans with narrower scope from directly (without using Provider<T>) being injected into e.g. singleton beans?

Advertisement

Answer

It looks like this can be achieved fairly simple using a custom BeanPostProcessor. Within the postProcessBeforeInitialization, you can simply check the scope of the bean and the scope of all dependencies. Here is a simple example:

@Component
public class BeanScopeValidator implements BeanPostProcessor {

  private final ConfigurableListableBeanFactory configurableBeanFactory;

  @Autowired
  public BeanScopeValidator(ConfigurableListableBeanFactory configurableBeanFactory) {
    this.configurableBeanFactory = configurableBeanFactory;
  }

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    String beanScope = configurableBeanFactory.getBeanDefinition(beanName).getScope();
    String[] dependenciesForBean = configurableBeanFactory.getDependenciesForBean(beanName);

    for (String dependencyBeanName : dependenciesForBean) {
      String dependencyBeanScope = configurableBeanFactory.getBeanDefinition(dependencyBeanName).getScope();

      // TODO: Check if the scopes are compatible and throw an exception
    }

    return bean;
  }
}

This example is still very basic and is not really convenient to use. Most prominently, it lacks the capability of defining which scope can be injected into which other scope. Thus I’ve created a more complete example here. Using this project, the following injections are allowed by default:

  • Singletons can be injected into everything
  • Everything can be injected into prototypes
  • AOP proxies can be injected into everything
  • Everything can be injected into beans of the same scope

If you want to allow a bean to be injected into another scope, it needs to be explicitly allowed by using a respective annotation:

@Bean
@Scope("prototype")
@InjectableInto("singleton")
MyBean getMyBean(){
  //...
} 
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement