3

A tonne of code at my company uses the javax.inject.Named annotation with the default value, which the Javadoc indicates is the empty string "".

For example:

@Named
public class Foo {
   ... 
}

This does not appear to add any value, since the empty string doesn't have any semantic meaning. If I remove the @Named annotations will there be any harmful effects?

The question What is javax.inject.Named annotation supposed to be used for? describes how @Named functions, but doesn't explain any special significance of the empty string, or why it would be necessary or beneficial to omit the actual name.

The question When should you explicitly name a Managed Bean? likewise talks about when you would want to use names to differentiate injectable beans, but doesn't provide any rationale for the use of the empty string as a name.

Can I delete these un-named @Named annotations without breaking anything?

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
0xbe5077ed
  • 4,565
  • 6
  • 35
  • 77
  • I think the implicit answer here is: This is not what `@Named` was intended for, and the code you describe is a misuse of `@Named`. I would go further and say that `@Named` itself is a mistake nearly 100% of the time. If you want an annotation parameterized by a string, you can make your own, but if you use `@Named` all over the place, you've replaced a *type system* based on annotation types with a "stringly typed" system, with all the lack-of-debuggability that that implies. – Daniel Pryden Jan 15 '19 at 17:13
  • It's worth noting that the Guice `@com.google.inject.name.Named` annotation, which AFAIK was the inspiration for JSR-330's `@javax.inject.Named`, did not have a default value for the string, so `@Named` without an argument would be a compile error. I don't know why the JSR-330 group decided to add a default value. – Daniel Pryden Jan 15 '19 at 17:15
  • 1
    @DanielPryden, even in your comments, you haven't actually answered the question. Saying "that is not what `@Named`" and "it is a misuse" doesn't actually answer the questions *Does it have any effect?* and the subsidiary question *Is it safe to delete it?* – 0xbe5077ed Jan 15 '19 at 17:23
  • @DanielPryden, in what way is asking, "Does X have any effect?" and the subsidiary question "Is it safe to delete X?" ambiguous? Can you please pinpoint the ambiguity so I can edit my question to remove it? – 0xbe5077ed Jan 15 '19 at 17:25
  • _The same applies to CDI beans, EJBs, JPA Entity classes, etc._. That's very much explicit. – Sotirios Delimanolis Jan 15 '19 at 17:30

2 Answers2

2

@Named (javax.inject.Named) is equivalent of @Component (org.springframework.stereotype.Component).

When used to annotated a class, it indicates that the class will be scanned and registered. If name is not given, DI framework will use the class type when injecting dependencies.

In short, you can't remove those @Named annotation. If you do, everything will be compiled as normal. However, at runtime, you'll get runtime error something like cannot find bean xyz.

chepukha
  • 2,371
  • 3
  • 28
  • 40
0

It's impossible to know if you will break anything without analyzing all the code that constructs injection keys and all the code that injects any of these bindings.

In some JSR-330 implementations (e.g. Dagger) it's not possible to use a @Named annotation with a value constructed at runtime, but in other implementations (e.g. Guice) it is possible and in fact commonly done.

For example, I could imagine a Guice module like:

public final class DynamicFooModule extends AbstractModule {
    private final String whichFoo;

    public DynamicFooModule(String whichFoo) {
        this.whichFoo = whichFoo;
    }

    @Override
    protected void configure() {
        Key<Foo> fooKey = Key.get(Foo.class, Names.named(whichFoo));
        Provider<Foo> fooProvider = getProvider(fooKey);
        bind(Foo.class).toProvider(fooProvider);
    }
}

This provides a binding for an unannotated Foo which delegates to a @Named(x) Foo, where x is determined by a constructor argument to the module -- which could be constructed at runtime, or derived from some default somewhere, etc.

You could imagine code building an injector like:

Injector injector = Guice.createInjector(
    ...,
    new DynamicFooModule(getSelectedFooConfig()),
    ...);

Where getSelectedFooConfig() might return "" as a default or fallback.

In a situation like that, @Named without any name could be a reasonable fallback value to use. If your application is doing anything like that, then it is not safe to remove the @Named bindings, because an un-annotated binding is not equivalent to a binding with an empty string.

I still would argue that this is not a good design: it would be better to use a dedicated qualifier annotation for this purpose (e.g. @ConfigBased("foo-config")) rather than just using @Named. If you were doing that then you could at least identify which strings were being used (or, better yet, eschew strings and use an enum instead).

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
  • If I understand what you are saying, at least in the Guice case I would have to provide an explicit funky binding in order for `@Named` to have any effect? – 0xbe5077ed Jan 15 '19 at 18:06