How do I configure JSR-330 @Provider and @Inject @Named("foo") String **programmatically** in Spring?

Thorbjørn Ravn Andersen picture Thorbjørn Ravn Andersen · Jan 3, 2011 · Viewed 9.2k times · Source

We have decided to use Dependency Injection with JSR-330 annotations for our future modularization efforts, and have been very pleased with the first deliverable based on Guice 2 SVN.

Now we need to ensure and document through unit tests that the constructions we need, also work in Spring when configured programmatically (we want the same refactoring support as with Guice so no XML files). I have problems with @Provider and @Inject @Named("foo") String but I have made plain @Inject work with:

ApplicationContext ctx = new AnnotationConfigApplicationContext(LIBL_Object.class, 
                                                                CORE_Provider.class);
this.object = ctx.getBean(LIBL_Object.class);

where LIBL_Object is the base class to be injected into, but the CORE_Provider does not register as I hoped within Spring.

The implementation of CORE_Provider is

package qa.jsr330.core;

import javax.inject.Provider;

public class CORE_Provider implements Provider<ProvidedInterface> {
    @Override
    public ProvidedInterface get() {
        return new CORE_Provided();
    }
}

and I want it injected into

package qa.jsr330.core;

import javax.inject.Inject;

public class LIBL_Object {

    private ProvidedInterface provided;

    public ProvidedInterface getProvided() {
        return provided;
    }

    @Inject
    public void setProvided(ProvidedInterface provided) {
        this.provided = provided;
    }
// Other stuff omitted.
}

Also we have found that we can pass configuration values very clearly using the @Named tag. This code looks like:

String hostname;
@Inject
public void setHostname(@Named("as400.hostname") String hostname) {
    this.hostname = hostname;
}

where we can then register this string with Guice using

bindConstant().annotatedWith(Names.named("as400.hostname")).to(value);

So the two questions are:

  • How do I register the @Provider class with Spring 3 programatically?
  • How do I register a string constant with Spring 3 so that @Named selects it properly?

Answer

axtavt picture axtavt · Jan 3, 2011

The short answer is: there is no such thing as programmatic configuration of Spring.

Despite the fact that both Spring and Guice support JSR-330 API and that Spring can be configured without XML now, their ideologies are still very different. Spring relies on static configuration, either in the form of XML files or annotated Java classes. Therefore straightforward attempt to adapt Guice-style configuration to Spring may produce difficulties.

Regarding the problem with Provider - Spring doesn't support javax.inject.Provider in the same way as toProvider() binding in Guice (by the way, this usage of Provider is not specified in JSR-330 docs). Therefore some Spring-specific annotations may be needed, for example

@Configuration
public class CORE_Provider implements Provider<ProvidedInterface> {
      @Override @Bean
      public ProvidedInterface get() {
          return new CORE_Provided();
      }
} 

Binding value coming from the outside may be difficult due to static nature of Spring configuration. For example, in your case, it can be done like this:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(IBL_Object.class);
ctx.register(CORE_Provider.class);
ctx.registerBeanDefinition("as400.hostname",
    BeanDefinitionBuilder.rootBeanDefinition(String.class)
        .addConstructorArgValue(value).getBeanDefinition());
ctx.refresh();