dpolishuk / atinject

Automatically exported from code.google.com/p/atinject
0 stars 0 forks source link

support custom injection annotations? #15

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
There are a number of annotations in a variety of frameworks both JCP standards 
(like JSR250, EJB3, JAXRS) as well as common open source libraries 
(like Spring and Camel) which define annotations which act as custom injection 
points. There's lots of them but for the purpose of this issue lets 
just consider @Resource. (Others include @PersistenceContext from JPA, @Context 
from JAXRS - there's quite a few of them around).

To perform injection of JSR250/EJB3 style values using JSR330 we'd currently 
need to annotate @Resource with @Qualifier. Then we could use it as...

public class Cheese {
  @Inject
  @Resource
  DataSource customerDb;

...
}

Now to JSR250/EJB3 folks this looks a bit noisy. They are more used to this...

public class NicerCheese {
  @Resource
  DataSource customerDb;

...
}

What if we could annotate @Resource with @Inject (or made @Inject optional when 
using an annotation annotated with @Qualifier). Folks could then 
ship a JSR330 aware version of JSR250 - or folks could do AOP/bytecode weaving 
magic on class load to retrofit it.

So then a standard JSR330 implementation would see @Resource and treat it as an 
injection point like the NicerCheese code above; acting as if the 
@Inject were present as in the Cheese code. This would mean we'd be supporting 
existing JSR250/EJB code with minimal impact on JSR330 
providers (they'd just have to look for @Inject/@Qualifier on annotations 
rather than just look directly for @Inject).

i.e. by allowing annotations to be annotated with @Inject - or by making 
@Inject optional on annotations annotated with @Qualifier - not only does 
code get a little less noisy (which a tool or IDE can easily visually identify 
injection points if so desired) - but we can then easily support all of the 
existing standard annotations used for injection points today.

Now you might think whats the big deal with supporting all that legacy code and 
frameworks out there; the big win would be we could immediately 
migrate all those mini-IoC frameworks which numerous RIs of JSR 
250/EJB/JPA/JAXRS and others have created and replace them all with just 
JSR330. While someone doing 100% pure JSR330 won't notice much difference; 
someone using a mixture of JSR330 and JSR250/EJB/JPA/JAXRS and 
others would have the benefit of a single IoC framework doing all of the IoC 
across all of the frameworks and annotation flavours; which to end 
users would lead to much better error handling & error messages - since a 
single unified IoC engine would be doing all IoC whether using the older 
or new & shiny annotations.

I don't want to hamper a new specification with legacy crap here; I'm just 
thinking this is an easy win; allow a single, simple level of indirection - let 
annotations be annotated with @Inject to define alternative injection points 
while also keeping user code concise - yet keeping things easy to 
understand from a programmer/tool perspective (as its purely a simple level of 
direction thats trivial to navigate). The added benefit is lots of 
frameworks and tools out there can then rip out all of their IoC code and just 
replace it with a JSR330 provider :)

Thoughts?

Original issue reported on code.google.com by james.st...@gmail.com on 9 Sep 2009 at 12:40

GoogleCodeExporter commented 9 years ago
If we don't require explicit @Inject, I guarantee you that people *will* find 
ways to avoid it 
everywhere (you're right: "too noisy", they'll say), and then goodbye to any 
hope of eyeballing 
injection points. That "single, simple level of indirection" might as well be a 
hundred complex 
levels for those of us who read (and write) code in simple text editors. If 
injection is indeed 
to be the new "new", I need to be able to take the source of a Java class and 
identify its 
injection points as easily and directly as I can currently identify its 
constructors.

Original comment by tpeie...@gmail.com on 9 Sep 2009 at 1:13

GoogleCodeExporter commented 9 years ago
An injection point should be explicit for sure just by looking at the code. Its 
just a question of where the annotation goes; on the injection point or on the 
qualifier 
annotation.

Already JSR330 supports a level of indirection; qualifier annotations are 
annotated with @Qualifier - so you need to comprehend the custom annotations 
used at an 
injection point to understand it already (even if you use a simple text 
editor). 

Given this - (whether this issue is accepted or not) - I'd recommend using a 
modern IDE which includes a ton of powerful code comprehension, navigation, 
visualisation & refactoring support rather than having to do 'levels of 
indirection in your head'.

To cater for the simple text editor crowd, should we also avoid the existing 
level of indirection with @Qualifier and force that to be added to the 
injection point as 
well to avoid the person reading code having to understand what the annotations 
mean?

e.g. 

public class VerboseCheese {
  @Inject
  @Qualifier // indicate that one of the other annotations is a qualifier to save you looking at the annotation definitions
  @Red
  DataSource customerDb;

...
}

There's a delicate balance of noise versus power here. 

I don't think adding a level of indirection with @Inject on annotations is that 
big a leap given we're already supporting annotated annotations with @Qualifier 
on 
annotations and this proposal will result in less noisy code - plus this 
apparently simple change will let us re-work all existing injection mechanisms 
defined by the 
JCP to be JSR330 compliant (assuming the legacy annotations get JSR330 
annotations).

BTW folks already use @Resource for example in JSR250 and EJB; I've not heard 
any complaints yet from folks using simple text editors using those specs, 
demanding an extra @Inject to be in there just in case.

Original comment by james.st...@gmail.com on 9 Sep 2009 at 3:29

GoogleCodeExporter commented 9 years ago
I like your proposal James, I think it effectively deals with 'legacy' IoC 
specifications in a clean fashion wrt the resultant code.

Original comment by Larry.Ca...@gmail.com on 10 Sep 2009 at 3:27

GoogleCodeExporter commented 9 years ago
I agree that this is an important problem to solve, but I don't think 
meta-annotating @Resource with @Inject is 
the best solution.

I'd prefer we permit JSR 330 implementations to support legacy annotations 
directly. The spec is wide open on 
the configuration side, so I'd prefer to leave that up to the injectors. Guice 
can already support @Resource 
(thanks James!) and its own "legacy" @com.google.inject.Inject annotation. 
Other injectors will have their own 
legacy infrastructure to support, and I don't think it's worth the complexity 
to bake this into the spec.

Original comment by limpbizkit on 10 Sep 2009 at 3:56

GoogleCodeExporter commented 9 years ago
well, for future specification versions of JSR-250 et al, I find it ok to 
further
meta-annotate annotations like @Resource or @PersistenceContext, but imho we 
should
not 'silently' change 'old' annotations. At least we should think _really_ long 
about
it - the risk that we could introduce big problems for existing projects is 
pretty high.

And btw, imho @Resource, @PersistenceContext and kinds are NO basic Java 
entities but
EE and therefore _way_ out of scope for this very spec!

This should be discussed in other EGs anyway. If common-annotations 2 will use
JSR-330 @Inject then it would be cool - but that's not our duty.

> or made @Inject optional when using an annotation annotated with @Qualifier
+1
That's exactly what is defined in JSR-299, and imho this is pretty straight 
forward. 

Tooling support is not really an argument against it here. If a tool is able to 
look
at @Inject, it will also be able to look if an annotation is meta-annotated with
@Qualifier.

Original comment by MarkStruberg@gmail.com on 10 Sep 2009 at 4:25

GoogleCodeExporter commented 9 years ago
@limpbizkit the thing is to support @Resource with say, Guice, we can only do
post-injection reflection to inject fields or call methods. Constructor 
injection is
never gonna be supported. 

e.g. this is not possible today with Guice...

public class Brie {
  public Brie(@Resource('blah') DataSource ds) {...}
}

Also as a result of us hacking this in 'around the back' - Guice is not really 
aware
of this injection taking place (so it won't be visible to DI tooling for 
example).

This proposal allows legacy & custom annotations to be supported easily by all 
JSR330
providers out of the box in a standard way using @Inject - it avoids developers 
of
frameworks or the RIs of JSR250/EJB/JPA/JAXRS/Servlet3 of having to write N 
custom
hooks to plug into each JSR330 provider (with different DI capabilities - e.g.
constructor injection never being possible with Guice).

The nice thing about this proposal is its a very minimal change to the JSR330
provider (looking for @Inject at the injection point and on its annotations - 
which
JSR330 already mandates for @Qualifier anyway); but it really helps unify all 
the
various DI mechanisms that exist today in the JavaSE platform into all the 
possible
JSR330 providers. I think that unification has lots of merit (having suffered 
lots of
pain already using various JCP standards which have forms of injection with 
various
DI containers today which offer varying degrees of plugin support).

Original comment by james.st...@gmail.com on 10 Sep 2009 at 4:26

GoogleCodeExporter commented 9 years ago
fair points! thinking about it I agree that the individual EE/SE specifications 
should own the responsibility for revising their injection annotations (or not)
to be meta annotated via @Inject ... as long as we permit/enable it from a 330 
perspective

Original comment by Larry.Ca...@gmail.com on 10 Sep 2009 at 4:44

GoogleCodeExporter commented 9 years ago
@james.strachan: "Constructor injection is never gonna be supported"
Well, we've had it for a few months, so never-say-never!
  http://code.google.com/p/google-guice/wiki/ToConstructorBindings

I can see the problem of getting N frameworks to work with M JSR 330 
implementations seems is infeasible. 
But rather than retrofitting the frameworks' existing annotations, would it be 
simpler to just migrate the 
application code to support the vanilla JSR 330 annotations? I don't really 
want to carry @Resource around 
with me for the rest of my life.

The broader problem is that each of these frameworks have their own interesting 
semantics. I'm skeptical that 
we can map the weird and wonderful behaviours of JSR250/EJB/JPA/JAXRS/Servlet3 
to the streamlined 330 
model.

Original comment by limpbizkit on 10 Sep 2009 at 6:55

GoogleCodeExporter commented 9 years ago
I'm with Tim and Jesse on this one.

I don't care about making things easier for injector implementors if it results 
in less 
maintainable code. We should keep 330 simple.

The indirection of @Qualifier is different. You only have to look for it when 
@Inject is 
present.

While it would be nice if we always view code in smart IDEs w/ JSR-330 support, 
the 
reality is that online code review tools are just one of several exceptions.

It also appears as though you're blurring the line between injection and 
qualifier 
annotations. To support your example:

  public class Brie {
    public Brie(@Resource('blah') DataSource ds) {...}
  }

You'd just need to annotate @Resource w/ @Qualifier.

Original comment by crazybob...@gmail.com on 10 Sep 2009 at 10:49

GoogleCodeExporter commented 9 years ago
@crazybob In that case it'd not work, you'd also need to add an @Inject onto it 
too
like this. 

 public class Brie {
    @Inject
    public Brie(@Resource('blah') DataSource ds) {...}
  }

Unless as @limpbizkit mentioned, for every occurrence of @Resource you wrote 
some
explicit binding code (or a provider method) which kinda misses the whole point 
of
the @Resource in the first place (to avoid having to write custom binding code 
in
guice/spring).

i.e. so this means none of the existing IoC related JCP specifications could be
supported out of the box with JSR330 (250, EJB, JPA, JAXRS) without littering 
@Inject
everywhere - which is a big shame IMHO.

BTW I don't buy the code review argument either; pretty much everyone knows what
@Resource is for from JSR250 or what @PersisenceContext is for from JPA or what
@Context is for from JAXRS - if not its very trivial to do a quick lookup in the
javadoc. We're talking a handful of annotations period in JCP specifications.

Maybe this just means 330 is always gonna be simple for "guice style" IoC only
(@Inject mandatory everywhere unless you hand craft binding/providers) and 299 
is
where the IoC unification of the JavaSE and JavaEE specifications goes then? 

Original comment by james.st...@gmail.com on 11 Sep 2009 at 7:55

GoogleCodeExporter commented 9 years ago
James, JSR 330 configuration is not the same as Guice configuration. We can 
write JSR 330 injectors that support 
@Resource and @Context naturally, using their existing semantics. But there's 
no need to pen that into the JSR 
330 spec, or to meta-annotate anything.

Original comment by limpbizkit on 11 Sep 2009 at 8:05

GoogleCodeExporter commented 9 years ago
Correct, Jesse. Users can already use JSR-330 and 250 together w/o any 
additional 
specification.

Original comment by crazybob...@gmail.com on 11 Sep 2009 at 8:15

GoogleCodeExporter commented 9 years ago
Another way to look at this: if @Inject is supposed to mark "places that can be
injected" then the current @Target list of constructors, methods, and fields is
correct. But you can't inject an annotation, so marking them with @Inject would
technically be at odds with the meaning (unless that was changed of course).

Also what happens if I have an annotation that can be put on a class (as well 
as the
usual fields, methods, etc.) and I decide to decorate it with @Inject - this 
could
confuse developers who might then expect to be able inject all members in a 
class
using this annotation. What if someone uses two levels of annotation 
indirection?

I can certainly see how decorating @Resource would be helpful, but I agree with 
Tim
that this could really diminish the benefits of @Inject (and also blur its 
meaning).

Original comment by mccu...@gmail.com on 11 Sep 2009 at 8:53

GoogleCodeExporter commented 9 years ago
FYI, the 299 group has done a lot of thinking about the relationship between 
Java EE
component environment injection, and the new-style typesafe injection, and this 
is
addressed in the 299 specification.

Original comment by gavin.k...@gmail.com on 11 Sep 2009 at 3:31

GoogleCodeExporter commented 9 years ago
I discourage users from using @Resource altogether. Your example should just be:

 public class Brie {
    @Inject Brie(DataSource ds) { ... }
  }

If you have more than one DataSource impl, use a qualifier:

 public class Brie {
    @Inject Brie(@ReadOnly DataSource ds) { ... }
  }

Original comment by crazybob...@gmail.com on 11 Sep 2009 at 8:20