JSR-299 & @Produces
Join the DZone community and get the full member experience.
Join For FreeI've been reading JSR-299 and I have a few thoughts.
First, a quote from chapter 1:
"The use of these services significantly simplifies the task of creating Java EE applications by integrating the Java EE web
tier with Java EE enterprise services. In particular, EJB components may be used as JSF managed beans, thus integrating
the programming models of EJB and JSF."
The second sentence made my ears prick up. Do I really want EJBs to be
the backing beans of web pages? To me that sounds like one is mixing up
responsibilities in MVC. Sure, there are people out there who say that
an MVC Controller should be skinny and the model should be fat (youtube ad).
Perhaps I'm old school, but I prefer my JSF page to have a backing
bean which is my view model and deals with presentation specific logic,
and when it needs transaction and security support, it can call through
to an EJB which deals with more businessy things.
The JSR then goes on to introduce the @Produces annotation. I don't like that annotation and the rest of this blog posting is about why.
When I need an object, like a service or a resource, I can get the container to inject it for me. Typically, I use the @Inject, @EJB, @Resource or @PersistenceContext
annotations. In the object which has such things injected, I can write
code which uses those objects. I let the container compose my object
using those pieces and other beans. There are many advantages, like
having code which is easily testable because I can compose the object
out of mocks if required, or like being able to configure its
composition rather than hard coding it. And we are all used to
composing objects like this, from the earliest days of Spring, if not
before.
Now, with @Produces, we have a new way to declare where things come from. Take the example in the JSR, where a Login bean is shown (chapter 1).
The Login bean "produces" the current user (page 4). Well, let's start with some code:
@Produces @LoggedIn User getCurrentUser()
If I read that code out loud, I say something with a similar meaning to: "The bean called Login produces the currently logged in user".
Well, that doesn't make too much sense to me, because coming at the
design from a pure OO point of view, starting with use-cases, there is
no Login bean mentioned in the use case, and even if there were, it does
not "produce" the user! The user exists before they login to the
software. They are simply authenticated and perhaps authorised after
login. The login component of the software (which could be represented
by the Login bean) can provide the details of the currently logged in
user. The syntax used in that code above has a poor semantic meaning,
in my view.
So if this is the modern trendy way of passing information around in a
JSF app, I have to ask myself whether we were not able to do such things
in the past? Or was it really hard to program before we had the @Produces annotation? Let's look at one solution; some code in a bean which needs to know the logged in user.
@Inject Login loginBean;
To use that, the Login bean would need to be declared as being @SessionScoped, and guess what - it is already marked so in the JSR example.
To access the current user, I would simply write code like this:
loginBean.getCurrentUser().getName() blah blah
Simple huh?
If you don't like the idea of injecting the Login bean, then why not simply create a session scoped bean called "CurrentUser"
and during login, set the authenticated user attributes into that
session scoped bean? Why go to the lengths of adding yet another way of
injecting objects, namely the @Produces annotation?
Because there is a different way of doing injection, the whole mechanism
has become more complex. I believe it is these types of complexity
which cause newbies to give up Java EE before fully understanding it,
and which cause existing Java advocates to give up and move to Grails,
etc.
To go with the @Produces annotation, we are given
qualifiers. Qualifiers are effectively type safe names used for
filtering, and as such, potentially better than String constants in an
interface somewhere used in conjunction with an @Named
annotation. Here is why I don't think Qualifiers should be used as the
norm, but rather as the exception. The class of object being injected
should have a suitable name to tell the reader what is going on.
Consider this code, which injects the current user which the login bean
produces:
@Inject @LoggedIn User currentUser;
There is unnecessary triplication going on in that line. "LoggedIn",
"User" and "currentUser". In the ideal world, I don't want three names
here, I want one. Java forces me to declare the type, which is actually
not really that necessary, because the compiler could, using
conventions, infer the type from the name. But let's not go there, and
instead accept that we have to declare a type. Why on earth then, do I
want to additionally declare a qualifier? I don't, it's a waste of
finger energy. Only when there is ambiguity would I be required to use a
qualifier. But if I have a session scoped model, which contained the
user, I could spare myself the energy of using a qualifier. I would
simply inject the model bean and pull the current user out of it. That
is what I have been doing for years without a problem. My Mum always
told me not to fix something which isn't broken.
At this JBoss getting started guide, they give an example of producing a random number, with code like this (in the producer):
@Produces @Random int next() { return getRandom().nextInt(maxNumber - 1) + 1; }
and this code in the consumer:
@Inject @Random int someRandomNumber;
Sure, it's a toy example. But what is wrong with injecting the Generator
bean (which contains that producer method), and calling the method on
it which generates the random number? I could even work with @Alternative if I wanted to decide on the actual implementation at deployment time.
For me, injecting the bean, rather than the value is very important for
readability. It gives the reader (maintainer) contextual information
about where that value is coming from. In my mind, the following code
is much better. It is quicker to write, because I don't need to create a
qualifier annotation.
@Inject Generator generator;
And then in that class, some code to use the generator:
generator.nextRandomInt();
Section 1.3.3 of the JSR gives an example of setting up the entity managers for injection into beans:
@Produces @PersistenceContext(unitName="UserData") @Users EntityManager userDatabaseEntityManager;
And then here, the code which uses that entity manager:
@Inject @Users EntityManager userDatabaseEntityManager;
Oh, and don't forget the code for the @Users annotation...
I end up with two extra files (the annotation and the producer). Is
all that code really better than the following, which is simply put into
a bean requiring the entity manager:
@PersistenceContext(unitName=Units.UserData) EntityManager userDatabaseEntityManager;
I'm just not convinced that the "modern" & trendy, yet complex JSR-299 way of doing it is better.
Section 3.3 then goes on to tell us a bit more about why producers exist and in which circumstances we might want to use them:
- the objects to be injected are not required to be instances of beans
Since almost every class is now a bean, I am hard pushed to think of a
case where I could not create a bean if I wanted something injected.
- the concrete type of the objects to be injected may vary at runtime
This is exactly what @Alternative and @Specialize annotations are for, and I do not need to use a producer to vary the concrete runtime implementation.
- the objects require some custom initialization that is not performed by the bean constructor
Adding the @Inject annotation to a constructor tells the
container which constructor to call when special construction is
required. How can a producer produce something which a bean couldn't?
Section 3.4.2 then talks about declaring producer fields. The example
they give shows the field having default (package) visibility, yet as
far as I can tell, any bean in the deployment bundle, even one outside
the producers package, can use the "product". This is a little like
indecently assaulting Java.
So to summarise, I guess I just want to put out a plea to future spec
authors. If you can't work out a way to do things simply, especially if
we already have that mechanism, please don't go and make things more
complicated than they need to be. I don't want to learn 92 pages of JSR
just to be good at using beans, when I already have to learn thousands
of other pages of JSR just to get working with Java EE.
From http://blog.maxant.co.uk/pebble/2011/11/07/1320702240000.html
Opinions expressed by DZone contributors are their own.
Comments