Craig Walls has been professionally developing software for over 14 years (and longer than that for the pure geekiness of it). He is the author of Spring in Action (now in its second edition) and XDoclet in Action, both published by Manning and is currently writing about OSGi and Spring-DM. Craig has posted 9 posts at DZone. View Full User Profile

When Good Annotations Go Bad

10.22.2008
| 14654 views |
  • submit to reddit

At one time not too long ago, I wasn't a big fan of annotations. But then I let my guard down and even started liking them. But now I'm starting to wonder if my dislike was a good thing.

Let's say that I have a class called Document. And let's say that a Document can be indexed into a searchable index using a IndexService class. And let's say that I have a Spring MVC controller class called SearchController that uses IndexService to search for Documents given some search criteria.

Now, just for fun, let's say that IndexService uses Compass to persist Documents to Lucene. And, as for Document, let's say that I chose to use Compass annotations such as @Searchable instead of the Compass' XML mapping.

One final bit of stage-setting: Each of these classes is in a separate project (a separate Maven module). Document is in a "domain" module, IndexService is in an "index" module, and SearchController is in a "web" module.

Okay, so SearchController has a dependency on both IndexService and Document. That's fine, because it will be dealing with both of those directly.

But, as luck would have it, SearchController also transitively depends on @Searchable and any other Compass annotations I use in Document. That means that I can't compile the web module without it having Compass in its build classpath (that is, as a dependency in its POM. Even though SearchController doesn't know about or need to know about Compass!!!

Doesn't this violate the law of demeter?

Someone please tell me that I'm wrong and show me the error in my ways. I want to be wrong here. But I can't seem to be able to build my web module without adding Compass to the list of dependencies...and that seems to be just plain wrong to me.

From http://www.jroller.com/habuma

Published at DZone with permission of its author, Craig Walls.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Mladen Maravic replied on Wed, 2008/10/22 - 2:35am

Actually, I dislike annotations for the very same thing: they add needles dependencies to code!

For example, suppose I have a client/server application. The one thing that my server and client parts of the code share is the business domain model - objects that represent business data. This domain model is nothing but a bunch of Java classes with getters and setters. But, since I want to use EJB3 on the server side, this objects would also get EJB3 annotations (like @Entity). But how do I use that same code in my client side? My client side doesn't understand the EJB3 annotations nor it should understand it! This is exactly why I don't use EJB3 annotations, but use XML configuration. This way, my applications are build out of three (ok, usually more than three...) projects: domain, server and client.

I don't like the idea of not using the same codebase for the domain project on the server and client since that in my experience leads to stupid mistakes. And I certanly don't want my client side to have any dependencies to EJB3. Even if it's just for annotations...

Still, I do use annotations in code which isn't shared like in the previous example. Annotations are certanly helpfull, but one should exercise common sense and not use them as a solution for every problem.

Andrew McVeigh replied on Wed, 2008/10/22 - 2:47am

I don't think it's a violation of the law of demeter (i.e. only talk to your friends) so much as bad packaging of dependencies.  couldn't the annotations all be put into a separate POM which both yourself and the implementation of compass depend upon?  I presume the annotations are currently in the same POM as the compass implementation?

 Andrew

Jose Noheda replied on Wed, 2008/10/22 - 4:39am

Well, it would be interesting if there was some clause that would inform the compiler to omit annotations if it cannot find them. But other than that, I can't see why they would break the law of demeter.

Bruno Borges replied on Wed, 2008/10/22 - 6:14am

I'm not sure about this, but it's an idea:

Have a profile in your pom.xml to compile annotated classes *without* processing those annotations with javac's option -proc:none.

This will give you the classes with no dependency on those annotations and then you can deploy them on the client-side with less jars, ie ejb3-annotations.jar (or something like that).

:-)

m2c

Fabrizio Giudici replied on Wed, 2008/10/22 - 6:39am

Hmm.... maybe I'm answering too in a hurry and I didn't understand well... but AFAIK when you have compiled a class C1 that is annotated with A, you can put C1 in the compile (and run) classpath for C2 without the need of putting A in C2 classpath.You only get warnings and A is ignored (as it's proper to do: annotations have a meaning only in a certain context, and in C2 context A is meaningless).

I' ve just  double checked, compiling a sample class X against a JAR that contains javax.persistence annotations (but not putting jpa.jar in the compiler classpath):

istral:/tmp> javac -classpath it-tidalwave-catalog.jar X.java
it/tidalwave/catalog/persistence/CategoryPB.class(it/tidalwave/catalog/persistence:CategoryPB.class): warning: Cannot find annotation method 'name()' in type 'javax.persistence.Table': class file for javax.persistence.Table not found
it/tidalwave/catalog/persistence/CategoryPB.class(it/tidalwave/catalog/persistence:CategoryPB.class): warning: Cannot find annotation method 'length()' in type 'javax.persistence.Column': class file for javax.persistence.Column not found
it/tidalwave/catalog/persistence/CategoryPB.class(it/tidalwave/catalog/persistence:CategoryPB.class): warning: Cannot find annotation method 'name()' in type 'javax.persistence.Column'

etc... Just warnings, the compilation is successful.

Lindsay Holman replied on Wed, 2008/10/22 - 7:14am

I don't see things the same as you Craig - I have never liked annotations :).  They simply do not achieve the same level of decoupling that you get from declarative methods.  If however, they provided some sort of markers to the JVM that could be populated or ignored at runtime, and the markers were standardised, so multiple providers could satisfy the runtime dependancy, we might be closer to something useful.  But, I am happy enough with xmlf files for now.

Brian Sayatovic replied on Wed, 2008/10/22 - 7:42am in response to: Mladen Maravic

I agree that annotations add dependencies to code. When all of a framework's meta data about how to use a class was in XML, it was also NOT in the class. Now that frameworks encourage you to add annotations to the class directly, the separation is gone. Now if you want to use a POJO as an EJB, you add EJB annotations. And if you want to persist it with Hibernate, you add Hibernate annotations. What if you want to switch to use that class in some other way? Fortunately, many frameworks still allow you to use external configuration or internalized annotations (e.g. the new servlet annotations can still be overriden by web.xml). But is this always intentional, or just to be backwards compatible for applications that were built before the annotations were available? Some of the language-level annotations don't bother me in domain classes, but framework ones do (EJBs = framework, not language). However, I suspect you can still use framework annotations by creating wrappers/subclasses around your POJOs. Thus, you're adapting between your non-framework-specific domain and the framework you want to host it in. Unfortunately, most examples and tutorials for these frameworks don't encourage this. They instead show simple "Hello, World!" examples where the annotations are directly in the domain code.

Guillaume Jeudy replied on Wed, 2008/10/22 - 8:27am

I don't know the classes you are talking about in your example so I dont know if it is applicable.

Here's the guideline I try to follow: if you only refer to interfaces in your code and keep those clean of annotations, Only put the annotations on the implementations. This way, other maven modules should only depend on the client jar which has the interfaces and *not* the implementations, therefore you are no longer transitively dependent on the annotations to compile other modules.

Josh Marotti replied on Wed, 2008/10/22 - 9:05am

Yup, double-edged sword.  Annotations remove all that xml config, but at the cost of dependencies.

 

I have this argument in my head everytime I use hibernate/jpa.  Do I added javax dependencies to my domain objects, which is entirely unnecessary, or do I hand write xml config files to make life cleaner? 

 

It really comes down to if you can justify reuse or not, and will that reuse be killed by the dependencies... 

Gregg Bolinger replied on Wed, 2008/10/22 - 10:41am

It depends?  Yea, that's a good answer.  Good and bad usage of Annotations depends on the context in which they are used.  For example, using an annotation like this:

@UrlBinding("/action/user/edit/{user.id}")

public class UserAction..... { }

is a lot better than wiring that in some XML configuration file.  On the other hand, I've seen some of the suggested annotation support for the next version of iBatis and I'm horrified.  So yea, it depends.

As to your concern over the law of demeter, I would agree that your annotations might be better off in their own module.  Even if you don't, I don't believe you've violated the law though.

And Jarnic replied on Wed, 2008/10/22 - 11:03pm

I would agree with Gregg Bolinger. I have this very same issue with using EJB3 annotations on domain objects... but yet I love how easy it is to use EJB3 annotations and get things working. So, for me.. the tradeoff is worth it. Not having to worry about XML and easily develop/deploy ejb3 is better.. to me, than trying to pass a back end domain object all the way to the front end.

But.. a good example of annotations is JAX-RS.. (JSR-311). The Jersey implementation for implementing RESTful services is so easy.. annotations make good sense to me in this case.. as they are used in the servlet front-end and thus I am not worried about passing the classes around.

So yah.. it's a toss up.. used on objects you'll pass around throughout the code across tiers.. it may not make good sense to annotate them with tier specific annotations. What I am not 100% sure about tho... can you compile the annotations like EJB3 in the front end.. but say at deployment you don't want to distribute the ejb3 annotations with your WAR file in the web tier.. will it still run (and ignore the missing imports) at runtime.. or will it choke because it can't find the ejb3 annotations dependent classes? See.. like someone else said.. if the JRE would ignore the annotations if they aren't found.. because in this case they wouldn't be needed on a domain object.. then it would at least allow you to only have to provide a compile-time classpath to the ejb3 classes.. but not ship them with your web tier app.

 

erwin de ley replied on Thu, 2008/10/23 - 5:13am

I also feel that opening the "box of annotations" has not been a good thing. It provides a mechanism for cluttering the code with lots of meta-info that does not belong there.

For some simple cases, they could be handy. And for those, the Java language should define std annotations.

But the trend for tools and frameworks to each translate their needs/features in proprietary annotations is dangerous. Especially in combination with all the entry-level user guides promoting the addition of annotations in domain classes, since it is "so much easier" and "cleaner to read" than if you need "duplicated effort" to define these things somewhere else (i.e. outside the domain classes).

AFAIK, the fact that something is expressed as an annotation or as an XML-fragment, or (preferably) as some other piece of java code, does not change the goal of the thing we're trying to express.

Mainly : configuration info. And configuration info should not be in the domain classes. As it indeed typically adds dependencies to the thing that we're trying to get configured (besides the aesthetic issues one could have with these annotations inside domain code).

And it also makes any kind of reuse of such domain classes in another application impossible if we would like to choose other technical constraints there (like e.g. another persistence fwk). (do I smell vendor-lock-in sneaking in via annotations?? ;-) )

So, as an alternative we could consider a setup as follows, maybe :

- write domain classes with the least amount of config clutter in there as possible (including annotations)

- write separate config definitions in externalized files/classes for each 3rd-party tool/fwk involved

And for people that prefer XML files : feel free! Similarly, for annotation-lovers : pls do use them, but not in the domain classes ;-)

And the result could be that we simply have the option to define cfg info externally in different formats : XML, pseudo-Java (i.e. java code with lots of annotations) or in any other format imaginable.

 

Mohammad Nour El-Din replied on Thu, 2008/10/23 - 7:48am

IMHO, annotations added a sort of development simplicity to developers. But I encourage people responsible for developing frameworks not to eliminate the facility of using a separate configuration file. Actually people of JEE did that smartly as they provided developers with the annotations to play as the default configuration but still one can override them with XML deployment descriptors. And more specially for the JEE world we still need the separate configurations files as there still need to define application server specific deployment descriptors otherwise we will have to include those application server specific annotations inside code. At the end, framework providers should provide developers with both techniaues and for developers they should plan which one to use.

Guillaume Jeudy replied on Thu, 2008/10/23 - 8:13am

I agree with most people on this thread that indeed we should avoid putting annotations (except standard java annotations) on the domain objects that are passed between layers. There is an even greater chance that these domain objects be shared across projects making this point even more important.

 As with all new technologies I think framework developers will mature their usage of annotations and in a few years from now we should see a good balance of how annotations should be used in layer specific code. As mentioned by someone else on this thread, annotations provide a good default that can always be overriden by XML descriptors. This approach adopted by EJB3, JPA/Hibernate is a good one and should be made standard across all main frameworks.

Silvio Bierman replied on Thu, 2008/10/23 - 11:25am

There are no good annotations. Instead of defining a proper meta-language facility in Java we where sent off with annotations. Tool developers would have taken anything at that point so they based their work on them. And here we are now in a situation where everyone with an open mind will agree that we have made a horrible mistake. There where continuous warnings all the way, also from people much smarter than me.

In Holland we have a saying "beter ten halve gekeerd dan ten hele gedwaald" which roughly translates to "better to back up halfway than continue on the wrong path". Might just be very applicable here...

 

Robin Bygrave replied on Thu, 2008/10/23 - 7:14pm

Hmmm, I think you need to be careful to talk about compile time dependancies vs runtime dependancies (and originally I'd say this article was about compile time dependancies)

In terms of compile time I'd go with Andrew in that to me it looks like a packaging issue.

In terms of runtime... I'm not sure if people have noticed that if you load a class (that has annotations on it) but those annotations are not available (in the runtime classpath - of your client JVM classloader say) then your class is loaded (no errors etc)... and the annotations not on the classpath are not visible (they silently disappear if you like).

That is.. in another JVM / Classloader with the annotations not in the classpath...

Class myClassWithAnnotations = ...

Annotation[] anns = myClassWithAnnotations.getAnnotations(); 

This array will only return annotations that are available in the classpath of the runtime classloader. In this sense I would suggest there is little difference at runtime between annotations and a xml resource. The way I see it is that if the client JVM at runtime is not dependant on the XML it does not need the annotations either -  and the converse is also true, if it needs the annotations then it also would have needed the XML.

Sounds like people see this differently... that is,  a runtime Dependancy on XML is better than annotations because why? 

Thanks - Rob.

Lindsay Holman replied on Fri, 2008/10/24 - 2:00am in response to: Robin Bygrave

Robin,

I prefer external configuration (can be xml, though it doesn't have to be) for a number of reasons.  Most importantly for me, is that it doesn't clutter my code - this my biggest issue with annotations.

Also, if I want to switch between the solution provider of my annotations, given a general lack of standardised annotations, this will often involve requiring a completely different set of annotations - cluttering the code further. What if there is overlap between the annotations used by different providers?  I can't have a single version of code that is portable between them. If my configuration is external, different configurations can exist happily, nicely separated.

As I said earlier, given more standardised annotations, it may be more workable.  We certainly have some already, and as more standards become annotation based, this will improve, but, if only to give me complete separation of concerns, my preference remains external configuration.

Robin Bygrave replied on Fri, 2008/10/24 - 3:09am in response to: Lindsay Holman

Thanks Lindsay, good points - I totally buy them. You got me thinking about the issue of what makes a good annotation vs what makes a bad annotation.

In terms of annotations that clutter my code... yes, there are some annotations that were "hit with the ugly stick". Sooo ugly that they become unworkable in that they badly effect the readability of the code IMO. These are generally the 'nested' annotations and/or have far too many attributes... unfortunately I can think of quite a few examples...

Basically there is a level of complexity where you have to say - "this should never be defined by annotation(s)". Perhaps that level of complexity is in fact very low... hmmm.

I'm trying to reconcile my preference for simple annotations with the acknowledgement that they are absolutely terrible once they go beyond very simple. Plus the other issues you mention... non-standard ones etc.

Thanks again... I have to do some more thinking now :)

Michael Bushe replied on Wed, 2008/10/29 - 8:06pm

Here's a thought - if having clean domain objects is important and you want the the flexibility to change vendors, then why not create domain objects free of annotations, then make a new .hibernate.* package by extending the annotation-free domain objects and overriding methods (with pass-through super calls) and add hibernate annotations to the overridden methods? 

To use something other than hibernate, you can make another .foo.* package and put those vendor's annoations in that package.  It's some extra work but your domain is clean, your dependencies are clear, and portability is in tact.  Theoretically almost all the work will be done in the domain objects, so it's reusable across vendors.    It would be a good idea to add tests to make sure each hibernate object overrides all the necessary domain object (reflection can give you this info).

Joe Farmer replied on Tue, 2008/11/11 - 6:19pm

It took 10 years for Java community to recognize that xml is bad for configurations. And what was it replaced with? Annotations. It will take another 10 years to recognize that it was a mistake, since configuration is spread now all over the place, classes get polluted in such a way that the real code gets unreadable, annotations from different frameworks will mix, classes will look like garbage cans. And, of course, dependencies, dependencies, dependencies ...  Hopefully, the next step will be coming back to separate configuration files but written in a diffenent language - much simpler than xml, may be similar to the annotations one.

Chris Hardin replied on Fri, 2010/09/17 - 9:10am

It's all what you have gotten accustomed to. Most developers who have been at it a while loathe the look of the annotations because it looks foreign. If you have started with annotations, then the same could be said of the XML configuration. Annotations simplified the amount of work we do, they made out code self-documenting and gave us compile time checking.

There is always more than one way to skin a cat. The mention of interfaces for the domain model is a great one if you are worried about dependencies. The gentleman that separated the discussions from compile to runtime is also correct. What do you care if your core model depends on say JPA or Validation Framework? These would only be necessary for compilation and wouldn't effect your application if you were just using the model as POJOs. 

Too often in our trade, we look at dependencies as a bad thing for no reason. Something engrained in us from earlier programming days says that having dependencies in your core model is bad, but there are really few valid reason that this is true. 

My Core Model always depends on the Persistence Annotations an Java Validation Annotations and a couple of others because my core model is usually used by several applications in the enterprise and I want to write my functionality once and in one place.

The resistance to annotations is just resistance to new ideas. I enjoyed the heyday of XMl configuration files and I thought it was the best way to do it and it took me some time to completely embrace a convention-driven design over the configuration-drive one, but I believe my code to be much easier to read and understand now as oppose to then. 

As with any technology, it can be overdone. XML configuration was overdone, e.g. Struts. Annotations could also be overdone and abused as well. 

If you think there is a better way to provide this type of metadata in Java or .NET, you should come up with proposals or ideas on what you think would make it perfect and submit them. I would definitely be open to switching if someone proposes something better.

 

 

 

 

 

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.