30

Spring's @Autowire can be configured such that Spring will not throw an error if no matching autowire candidates are found: @Autowire(required=false)

Is there an equivalent JSR-330 annotation? @Inject always fails if there is no matching candidate. Is there any way I can use @Inject but not have the framework fail if no matching types are found? I haven't been able to find any documentation to that extent.

dur
  • 15,689
  • 25
  • 79
  • 125
Eric B.
  • 23,425
  • 50
  • 169
  • 316
  • 1
    Really not a good idea: they choose to *not* do it in CDI in order to make sure that all dependencies are there. Should you really have a null check on a dependency? Seems like an architecture problem, and some solution could be to inject an interface, which could have a no-op implementation. Null if not found is really bad design: is it not found on purpose, or because of a programming mistake? – ymajoros Aug 21 '17 at 09:40

6 Answers6

38

You can use java.util.Optional. If you are using Java 8 and your Spring version is 4.1 or above (see here), instead of

@Autowired(required = false)
private SomeBean someBean;

You can just use java.util.Optional class that came with Java 8. Use it like:

@Inject
private Optional<SomeBean> someBean;

This instance will never be null, and you can use it like:

if (someBean.isPresent()) {
   // do your thing
}

This way you can also do constructor injection, with some beans required and some beans optional, gives great flexibility.

Note: Unfortunately Spring does not support Guava's com.google.common.base.Optional (see here), so this method will work only if you are using Java 8 (or above).

Utku Özdemir
  • 7,390
  • 2
  • 52
  • 49
  • 2
    Interesting option. I wasn't on Java8 when I originally posted the question but I like the principle. Thanks for pointing it out. I guess technically, there is still no real option using JSR330, but this is a good use of the J8 advantages to get around it. – Eric B. Mar 16 '16 at 13:55
  • I am using Java 8 and Spring 4.2.7, and it works like a charm. But is the spec say how to manage "Optional" type? By the way, it is a very reasonable implementation. I like. – mcoolive Oct 19 '16 at 08:58
  • Of course, brilliant. Just what I needed and much cleaner than an optionally null bean. – Andrew Spencer Sep 06 '17 at 11:27
  • 1
    Great answer! Isn't the annotation parameter called `required` though? – Henrik Aasted Sørensen Nov 28 '17 at 09:16
  • @UtkuÖzdemir: That was incredibly quick for a 1.5 year old answer! Cheers! :) – Henrik Aasted Sørensen Nov 28 '17 at 09:20
  • 1
    But you should be aware that using the `java.util.Optional` as a field is discouraged in general. It does not even implement the `Serializable` interface. – Forketyfork Nov 28 '17 at 17:01
  • @SergeyPetunin yes, true, I used it for the sake of example. Better to use constructor injection and a nullable field. By the way, you should be aware that using Java Serialization is discouraged in general. – Utku Özdemir Nov 28 '17 at 17:11
  • Sure, you probably wouldn't want to serialize the bean yourself, but the framework could do this at some point in time, for example, if it is a session-scoped bean. – Forketyfork Nov 28 '17 at 17:20
16

No... there is no equivalent for optional in JSR 330... if you want to use optional injection then you will have to stick with the framework specific @Autowired annotation

dur
  • 15,689
  • 25
  • 79
  • 125
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
  • 1
    [I can't seem to find the spec document for the JSR330, if there is one.](http://www.jcp.org/en/jsr/detail?id=330) Do you know the reasoning behind the decision not to include a kind of `@Optional` feature? – Sotirios Delimanolis Oct 21 '13 at 03:47
  • 1
    >>Do you know the reasoning behind the decision not to include a kind of @Optional feature?<< I believe the reason is existance of Provider's. javax.inject.Provider.get() MAY return null, so if one injects Provider, one can achieve the same result as with optional injection – Valentin Kovalenko Feb 26 '15 at 10:38
13

Instance injection is often overlooked. It adds great flexibility. Check the availability of the dependency prior to getting it. An unsatisfied get will throw an exception which is expensive. Use:

@Inject
Instance<SomeType> instance;
SomeType instantiation;

if (!instance.isUnsatisfied()) {
    instantiation = instance.get();
}

You can restrict the injection candidates as normal:

@Inject
@SomeAnnotation
Instance<SomeType> instance;
Steve Brewin
  • 221
  • 3
  • 6
  • See this answer by Giovanni Silva – Tunaki Feb 24 '15 at 21:52
  • Thanks! I didn't know about the `javax.enterprise.inject.Instance` class. But what JSR/pkg is it part of? It doesn't seem to be part of JSR-330. Is this a JEE6 specific thing? – Eric B. Nov 09 '15 at 15:05
  • 1
    java.enterprise.inject.Instance is specified in JSR 299: Contexts and Dependency Injection for the JavaTM EE platform. As the title states, it is only applicable to the JEE platform. JSR 330: Dependency Injection for Java is a much lighter specification with no reliance on the JEE platform. It is a shame that JSR 330 did not incorporate all of the non JEE specific features of JSR 299, such as java.enterprise.inject.Instance. Why this did not happen I’ll leave to others to conjecture. – Steve Brewin Nov 13 '15 at 17:50
7

It's is possible to create a optional injection point!

You need to use injection lookup as documented in http://docs.jboss.org/weld/reference/latest/en-US/html/injection.html#lookup

@Inject
Instance<Type> instance;

// In the code
try {
   instance.get();
}catch (Exception e){

}

Or even all instances of a type

@Inject
Instance<List<Type>> instances

Also the get() method is lazy evaluated if you need. The default Injection evaluates at Startup time and throw an exception if no beans are found that could be injected, the bean will be injected at runtime of course, but the application will not start if this is impossible. In the documentation you will find more examples, including how to filter the instances injected and much, much more.

Giovanni Silva
  • 705
  • 5
  • 17
5

The AutowiredAnnotationBeanFactoryPostProcessor (Spring 3.2) contains this method to determine if a supported 'Autowire' annotation is required or not:

    /**
     * Determine if the annotated field or method requires its dependency.
     * <p>A 'required' dependency means that autowiring should fail when no beans
     * are found. Otherwise, the autowiring process will simply bypass the field
     * or method when no beans are found.
     * @param annotation the Autowired annotation
     * @return whether the annotation indicates that a dependency is required
     */
    protected boolean determineRequiredStatus(Annotation annotation) {
        try {
            Method method = ReflectionUtils.findMethod(annotation.annotationType(), this.requiredParameterName);
            if (method == null) {
                // annotations like @Inject and @Value don't have a method (attribute) named "required"
                // -> default to required status
                return true;
            }
            return (this.requiredParameterValue == (Boolean) ReflectionUtils.invokeMethod(method, annotation));
        }
        catch (Exception ex) {
            // an exception was thrown during reflective invocation of the required attribute
            // -> default to required status
            return true;
        }
    }

In short, no, not by default.

The method name that is being looked for by default is 'required', which is not a field on the @Inject annotation, thus, method will be null and true will be returned.

You may be able to change that by subclassing this BeanPostProcessor and overriding the determineRequiredStatus(Annotation) method to return true, or rather, something 'smarter'.

nicholas.hauschild
  • 42,483
  • 9
  • 127
  • 120
0

Spring supports @Nullable with @Inject:

@Inject
@Nullable
void setSomeBean(SomeBean someBean)
bernhof
  • 6,219
  • 2
  • 45
  • 71