@Specializes in Spring

1.3k views Asked by At

CDI has the feature of Specialization, and I'm looking for that in the Spring world.

Details. In CDI, the @Specializes annotation allows one to change the behaviour of a bean just by overriding it. This is completely transparent to users of that bean, e.g. if we'd have

public class OneBean {
  public String whoAmI() { return "OneBean"; }
}

@Specializes
public class AnotherBean extends OneBean {
  @Override
  public String whoAmI() { return "AnotherBean"; }
}

we could

public class SomewhereElse {
  @Inject
  OneBean oneBean; // we know nothing of AnotherBean here!

  public void guessWhosThere() {
    return oneBean.whoAmI(); // yet it returns "AnotherBean"
  }
}

This gets really useful as soon as OneBean is actually used with and without AnotherBean. For example, if OneBean is in one.jar and AnotherBean is in another.jar, we can change the bean's behaviour just by reconfiguring the classpath.

Question. Does something like Specialization also exist in Spring?

I could only find the @Primary annotation, which however has a different semantics: @Primary does not replace one bean, but only marks one of multiple alternatives as the primary one. Especially, as I understood, I could not build a deep inheritance hierarchy as it's possible with @Specializes.

3

There are 3 answers

0
Pavel Pscheidl On BEST ANSWER

Short answer In Spring 4, this is not possible. Period. Still, in 2016, nothing like this is possible with Spring's obsolete dependency injection model.

3
Maksym On

Seems like there is no similar annotation in spring, but you can achive it via @Qualifier.

Beans:

@Resource("oneBean")
public class OneBean {
  public String whoAmI() { return "OneBean"; }
}

@Resource("anotherBean")
public class AnotherBean extends OneBean {
  @Override
  public String whoAmI() { return "AnotherBean"; }
}

SomewhereElse:

public class SomewhereElse {
  @Autowired
  @Qualifier("anotherBean")
  OneBean oneBean;

  public void guessWhosThere() {
    return oneBean.whoAmI(); // returns "AnotherBean"
  }
}

Edited.

Also you can develop your own annotation and use it in BeanPostProcessor, look at spring docs here

OR even better to use CustomAutowireConfigurer, see here

0
C. Asselmeyer On

With Spring boot, you could probably get a similar result by leveraging its auto-configure mechanism, e.g. with a bean condition such as @ConditionalOnMissingBean:

public class OneBean {
  public String whoAmI() { return "OneBean"; }
}

@Configuration
public class OneConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public OneBean getBean() { return new OneBean(); }
}

@Component
public class AnotherBean extends OneBean {
  @Override
  public String whoAmI() { return "AnotherBean"; }
}

However, you would have to make sure that all configurations are built accordingly if you don't know for sure which ones will be specialized:

public class OneBean {
  public String whoAmI() { return "OneBean"; }
}

public class AnotherBean extends OneBean {
  @Override
  public String whoAmI() { return "AnotherBean"; }
}

public class YetAnotherBean extends AnotherBean {
  @Override
  public String whoAmI() { return "YetAnotherBean"; }
}

@Configuration
public class OneConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public OneBean getBean() { return new OneBean(); }
}

@Configuration
public class AnotherConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public AnotherBean getBean() { return new AnotherBean(); }
}

@Configuration
public class YetAnotherConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public YetAnotherBean getBean() { return new YetAnotherBean(); }
}
// and so on...