Spring 4.3: Introducing the InjectionPoint class

Did you know that Spring Framework 4.3 comes with a “hidden” gem: the InjectionPoint class ?

At the time of writing this post, InjectionPoint is nowhere to be found in the Spring Framework Reference Documentation [2].

About the InjectionPoint class

The InjectionPoint class [1] is a new feature that you can use in @Bean producer methods. It is specifically useful in @Bean methods that produces prototype scoped beans. And with it, you can get detailed information about the “places” into which your beans are injected. Said in another way:

@Bean methods can now be made context aware

If you know about Contexts and Dependency Injection (CDI) – then you may have heard about such a feature before. It is in fact an “oldie” in that context.

Example

I’ve prepared an example on GitHub – consult that for the source code in its full context. The example is based on Spring Boot 1.4 and therefore Spring Framework 4.3. Please bear with me …. in lack of a better example…

Imagine you want to inject a greeting into your GreeterService:

@Service
public class GreeterService {

  @Autowired @Greeting(language = Language.EN)
  private String greeting;

  public String sayHello(String caller) {
    return String.format("%s, %s", greeting, caller);
  }

}

… here is one way you could implement it:

@Configuration
public class MyBeanConfig {

  @Bean
  @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  public String greeting(InjectionPoint ip) {

    Greeting greeting = findAnnotation(ip.getAnnotatedElement(),
                        Greeting.class);

    return (Language.DA == greeting.language()) ? "Hej Verden" : "Hello World";
  }

}

Notice how this @Bean method uses the InjectionPoint to get access to the annotation on the dependency injection target field (GreeterService.greeting). The Greeting annotation is some annotation I dreamt up – super simple (not even a Qualifier).

In addition to the annotations on the dependency injection target field (/method/constructor…) you can also get the class object of the containing class (GreeterService in the above example). Take a look at the JavaDoc for further information [1].

That’s it! Now, imagine what you can do with it 😉 …

Relation to scope

I propose that InjectionPoint is specifically designed to work with prototype scoped beans. You may think that’s weird – but think about it this way:

  • If you use InjectionPoint in a singleton scoped @Bean method…
  • And if you have multiple places where the bean is injected…
  • Then how would Spring know which InjectionPoint to hand to you?

You know what? I actually tested that…and it turns out I was given the first InjectionPoint. To be completely honest: that surprised me. I actually would have expected an exception from the container. But no – I just got “the first” injection target encountered by the container. Imagine what use you have of that. My best guess is: no use at all.

CDI, by the way, only allows InjectionPoints when used with the dependent scope. Please take into account that CDI’s dependent scope and Spring’s prototype scope are roughly equivalent.

References

[1] Spring Frameworks JavaDoc on the InjectionPoint class:
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/InjectionPoint.html

[2] Spring Framework Reference Documentation:
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/

 

Tagged with: ,
Posted in Spring
13 comments on “Spring 4.3: Introducing the InjectionPoint class
  1. Green Luo says:

    I was curious what will happen if I have another `FarewellService`:

    “`
    @Service
    public class FarewellService {

    @Autowired @Farewell(language = Language.EN)
    private String farewell;

    public String sayBye(String caller) {
    return String.format(“%s, %s”, farewell, caller);
    }

    }
    “`

    How does `MyBeanConfig`tell springframework that I need to inject a farewell message so it doesn’t try to call `MyBeanConfig::greeting` method and trigger a NPE because I don’t have the `@Greeting` annotation on the `farewell` field in my `FarewellService`.

    So I downloaded your source code and tried that. It works all good, and seems like Spring is using the name of the inject field, say `farewell` and `greeting` to look for factory methods in `MyBeanConfig`, which is nice in most cases but might cause trouble when you want to inject two different object into the services with fields that has the same name, e.g. `RemoteServiceA.endpoint` and `RemoteServiceB.endpoint`

    • moelholm says:

      Hi,

      (You are correct in your observation: Spring will fallback to autowiring by name if it ends up with multiple candidates for an injection target. If no bean then matches the injection target name (member field name fx) – then you will receive an error.)

      I believe what you are looking for is something called qualifier annotations. You use qualifier annotations to help Spring choose the appropriate bean for an injection target.

      If you annotate your @Farewell annotation and “my” @Greeting annotation with Spring’s @Qualifier annotation – then you would have converted them into qualifier annotations! Also, don’t forget to add the annotations on the appropriate @Bean methods as well. Like this:

      @Bean @Farewell String farewell( ) { return “farewell”; }
      @Bean @Greeting String greeting( ) { return “hello”; }

      This way – when you write:

      @Autowired @Farewell String msg;

      ..then Spring will know that it is the “farewell” bean it should choose. The bean “greeting” doesn’t qualify – since it doesn’t list the @Farewell qualifier annotation.

      If you try out the above example (converting them to qualifiers) – then please leave out any attributes in the injection sites (language). Spring doesn’t support the equivalent of CDI’s @Nonbinding feature. Without it – you need to match the exact annotation attribute values on the @Autowired injection site with the @Bean producer site. One day…hopefully… Spring will get this support as well.

      • jhoeller says:

        As for non-binding attributes, we haven’t had a single request for this from a Spring user yet – over nine years – as far as I’m aware. The only references to it came from the CDI expert group, and my reaction was that this simply isn’t commonly applicable in Spring’s model, neither for qualifier annotations nor for interceptor bindings (where it even creates a semantic conflict with Spring’s existing arrangement).

        In general, qualifier annotations are much less common in Spring than in CDI. In idiomatic Spring, if the type declaration isn’t specific enough, we recommend to make it specific enough if possible (e.g. through a type variable), or use our ‘primary’ flag, or use a ‘@Value’ annotation with a property placeholder, or a custom qualifier annotation… but only really as a last resort. Since custom qualifier annotations are crafted for a specific purpose here, they are not expected to have attributes that we need to ignore for that purpose.

        For interceptor bindings, we do not use the same comparison algorithm to begin with: Common Spring interceptor arrangements react to the presence of a plain annotation type, parsing all of its attributes within the interceptor. If some attributes are relevant for interceptor matching, it is the corresponding pointcut’s job to selectively match those. In other words, the annotation does not have to be self-descriptive for binding purposes in such a scenario; it’s rather the pointcut implementation knowing about attribute bindings.

      • jhoeller says:

        Looking at your example at https://gist.github.com/nickymoelholm/4317e93f18b02e9582afefc152debb44, I see your point and have commented along those lines there. Frankly, you are the first one to raise such a scenario with us, but point taken, a first-class way to model such scenarios in Spring looks like a fine enhancement request.

        However, I’m just not a fan of baking such usage-specific matching characteristics into an annotation type, in particular for interceptor bindings. For example, looking at JTA 1.2’s @Transactional, why is the TxType attribute binding but the other attributes are not? This seems like an implementation detail – individual interceptors for each transaction type – leaking into the common annotation declaration. In Spring’s support for that same JTA 1.2 @Transactional annotation, we ignore @Nonbinding completely, with our pointcut simply binding the same interceptor for all occurences… and that interceptor then evaluating the TxType attribute, adapting it to Spring’s transaction management abstraction.

  2. Hi Nicky, good catch, we need to cover this in our reference documentation. I’ve created the following ticket for it: https://jira.spring.io/browse/SPR-14797

    As for using this feature with arbitrary beans scopes, the general rule is: We’re consistently propagating the injection point that triggers the creation of the current instance. So in case of a singleton bean (or session/request scoped bean), you’ll get the injection point that actually triggers the creation of the current bean instance and therefore the invocation of the corresponding factory method. For lookups of existing bean instances, we’re not calling the factory method to begin with. So it’s not about first vs second etc, it’s about triggering the creation of the instance versus simply retrieving an existing instance. Since the factory method is only invoked in the creation case, there is no semantic ambiguity there.

    While this is certainly most useful with prototype beans, it can be applied to other scopes e.g. for tracking purposes: Depending on the initialization order and the selective inclusion/exclusion of certain beans (very common in a Boot scenario), a bean’s creation may get triggered from different angles. In some such scenarios, it may even make sense to react to the particular dependency declaration that triggered the creation of a particular (lazy) singleton bean, even if later injection points might simply receive the existing bean instance from then onwards. Since bean initialization is strongly ordered and highly customizable in Spring, in particular when not using classpath scanning, the outcome of a such an InjectionPoint arrangement is predictable and reliable enough even then.

    In other words, there simply was no hard reason to restrict the InjectionPoint argument feature to prototype beans. With clean semantics even in other Spring-managed scopes, we decided to leave it up to the application developer to make some use of it when desired.

  3. jhoeller says:

    Hi Nicky, good catch, we need to cover this in our reference documentation. I’ve created the following ticket for it: https://jira.spring.io/browse/SPR-14797

    As for using this feature with arbitrary beans scopes, the general rule is: We’re consistently propagating the injection point that triggers the creation of the current instance. So in case of a singleton bean (or session/request scoped bean), you’ll get the injection point that actually triggers the creation of the current bean instance and therefore the invocation of the corresponding factory method. For lookups of existing bean instances, we’re not calling the factory method to begin with. So it’s not about first vs second etc, it’s about triggering the creation of the instance versus simply retrieving an existing instance. Since the factory method is only invoked in the creation case, there is no semantic ambiguity there.

    While this is certainly most useful with prototype beans, it can be applied to other scopes e.g. for tracking purposes: Depending on the initialization order and the selective inclusion/exclusion of certain beans (very common in a Boot scenario), a bean’s creation may get triggered from different angles. In some such scenarios, it may even make sense to react to the particular dependency declaration that triggered the creation of a particular (lazy) singleton bean, even if later injection points might simply receive the existing bean instance from then onwards. Since bean initialization is strongly ordered and highly customizable in Spring, in particular when not using classpath scanning, the outcome of a such an InjectionPoint arrangement is predictable and reliable enough even then.

    In other words, there simply was no hard reason to restrict the InjectionPoint argument feature to prototype beans. With clean semantics even in other Spring-managed scopes, we decided to leave it up to the application developer to make some use of it when desired.

    • moelholm says:

      Hi Juergen,

      Thank you for clarifying the behavior – specifically for non prototype scoped beans.

      I am happy to hear that you have made a deliberate choice to allow InjectionPoints in @Bean methods for any scope (and that it is therefore not a bug in the validation process).

      I will update the post to send this message as well.

  4. Green Luo says:

    Hey @moelholm,

    I’ve copied your sample on Github and implemented using [Genie](https://github.com/osglworks/java-di), my JSR330 DI solution:

    https://github.com/greenlaw110/hello-genie-injectionPoint

    Cheers,
    Green

  5. greenlaw110 says:

    Actually I found Genie suffer the same issue with Spring. We need to implement the `NonBinding` semantic to qualifiers …

    When I change the language from “EN” to “DA” my test case failed 😦

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: