Issues with depepency injection in latest frameworks.

Here I am going to show some of important limitations to Java and Guice approached to dependency injections, particularly where wiring logic should go.

Java Dependency Injection

public interface LogService {
public void log(String result);
}

@Named(“FileLogService”)
public class FileLogService implements LogService{
public void log(String result){}
}

@Named(“ConsoleLogService”)
public class ConsoleLogService implements LogService{
public void log(String result){}
}

public class LogClient{

@Inject
@Named(“FileLogService”)
private LogConsole logConsole = null;

public void doSomething(){
logConsole.log(“doSomething”);
}
}

This is one way of injecting dependency into client code using Java annotation. As you can see we just hard coded LogClient to FileLogService and there is no way to have LogClient use ConsoleLogService.

Qualifiers wont help here either since they are same as @Named except instead of String it uses annotation to hard code LogClient to particular instance of LogService.
Here is example with Qualifiers.

public interface LogService {
public void log(String result);
}

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface FileLogService {}

@FileLogService
public class FileLogService implements LogService{
public void log(String result){}
}

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface ConsoleLogService {}

@ConsoleLogService
public class ConsoleLogService implements LogService{
public void log(String result){}
}

public class LogClient{

@Inject
@FileLogService
private LogConsole logConsole = null;

public void doSomething(){
logConsole.log(“doSomething”);
}
}

Guice

In Guice the wiring logic is based on number of Binding and Provider implementations.

Linked Bindings

public class LogModule extends AbstractModule {
@Override
protected void configure() {
bind(LogService.class).to(FileLogService.class);
//we cant do this
bind(LogService.class).to(ConsoleLogService.class);

}
}

With Linked Bindings we cant even register more then one implementation of LogService interface thus again hard coding LogClient to FileLogService.

Instance Binding

public class LogModule extends AbstractModule {
@Override
protected void configure() {
bind(LogService.class)
.annotatedWith( Names.named(“FileLogService”) )
.toInstance( new FileLogService() );
bind(LogService.class)
.annotatedWith( Names.named(“ConsoleLogService”) )
.toInstance( new ConsoleLogService() );
}
}

public class LogClient{

private LogService logService = null;

@Inject
public void setLogService(@Named(“FileLogService”) LogService logService) {
this.logService = logService;
}
}

Same story here, we are limited to only one implementation of LogService. We could use more generic name in Names.named but we are still limited to one instance of LogService for every instance of LogClient (we cant configure multiple instance of LogClient with different instances of LogService).

Providers

They are just factories that will still create only one particular instance for LogClient. Event worse, now instead of coding our client to service Java type we are forced to code against provider Java type.

Summary

No matter what approach you take you still end up hard coding your client with particular instance of service it uses. The above examples dont cover all dependency injection use case but all end up with the same issue. There are only 2 ways to do dependency injection the right way:

1. Use plain Spring XML based injection where you define and wire instance manually (no autowiring). If you dont like XML, you can still do the whole thing in Java.

2. Have something like Guice module but instead of one module instance for the whole application use multiple instances configured with one particular LogService.
But you have to be careful with selection of clients and services so not to end up requiring a service that is already registered with different instance.

Finally, all this new dependency injection business is against basic OOP principles. You should never add anything more than a Java type to your client code dependencies (all client code should care is that there are set of method on its dependency it can call), and services should never have any knowledge of client using those. Their wiring should be done only by third party object and it should provide flexibility of wiring different instances of Java client class to different instances of Java services class.

14 comments to Issues with depepency injection in latest frameworks.

  • Tsolak Petrosian

    Thank you for the reply.

    I’ll start with the final paragraph – annotations are metadata. Metadata is either external or internal. But it doesn’t have to do with >>OOP. Some prefer XML, some prefer the ease and compile-time checking of annotations. I see the point in both (having used both)

    I am not arguing against annotations in general thought I much prefer to push this metada somewhere external like XML. A few times (mostly with Hibernate) annotation and their lake of inheritance support have given me problems in the past and forced to fall back to XML definition.

    About not being able to dynamically decide what to inject – again incorrect. First, with spring it is also not rather easy – you’d have >> to hardcode your dependencies in the xml, which is pretty much the same – the xml goes with your deployment. Yes, you can use a >>FactoryBean to decide dynamically what to inject.

    I am sorry but its not “pretty much the same”. XML is not hardcoding, hardcoding happens when Java class contains data that is specific to particular application or environment and cannot be reused in another application.
    XML is your application and thats the place all the wiring happens. Your Java class can (and most likely) be in separate jar that will be reused in different application and wired differently (i.e in one case with FileLogService another with
    ConsoleLogService) or even wiring differently in the same application.

    And in CDI you have quite a lot of ways do do that:
    - use a Producer to instantiate and return whichever implementation you like. You can add a InjectionPoint argument to your producer >>method and you have the details of the class and field where the injection is happening.
    - use Instance, and dynamically select the desired instance using instance.select(qualifier)
    I’d suggest to ask a question on stackoverflow or in the CDI forums whenever you have doubts about these things.

    The mecahnism you are talking still forces client to contain logic of slecting particular instance of the service.
    I challenge you to show me how you can do the following configuration using the mechanisms above:

    <bean id="fileLogService" class="FileLogService"/>

    <bean id="consoleLogService" class="ConsoleLogService"/>

    <bean id="logClient1" class="LogClient">
       <property name="logService" ref="fileLogService"/>
    </bean>

    <bean id="logClient2" class="LogClient">
       <property name="logService" ref="consoleLogService"/>
    </bean>

    And to conclude – I’m a strong spring supporter, and a but anti-JavaEE, but one can’t deny the power and thought that’s put into CDI (contexts and dependency injection, which you call java DI)

    Again I am not against CDI, contrary I have used CDI since before Spring with emerging DI containers, I am just showing the limitations of new CDI containers that avoid traditional third party wiring.

  • Tsolak Petrosian

    I am surprised its personally Gavin King:)

    As for XML not being “hardcoding”, that’s simply nonsense, and no-one seriously believes that anymore. XML is code like any other code.

    I want to start from this comment since I suspect I am still not clear on the actual issue.
    Please not that we are talking here about Spring context XML files.

    - XML is a code, yes. I have never denied it.
    - XML is not hard coded to particular application since it is the application by itself. In contrary to LogClient I am not going to be reusing my XML definition file.

    The feature of CDI that you are missing is @Alternative and (@Alternative stereotypes). @Alternative lets you select the particular implementation you’re interested in using the beans.xml file. It’s a much cleaner, less verbose solution than Spring’s XML.

    I just cant see how this can be clean, it does not even solve the configuration I had in the previous comment.

    Let me show an example using @Alternative:

    @Alternative
    public class FileLogService implements LogService{
    public void log(String result){}
    }

    @Alternative
    public class ConsoleLogService implements LogService{
    public void log(String result){}
    }

    public class LogClient{

    @Inject
    private LogConsole logConsole = null;

    public void doSomething(){
    logConsole.log(“doSomething”);
    }
    }

    and in beans.xml

    <beans xmlns="http://java.sun.com/xml/ns/javaee&quot;
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd&quot;>

        <alternatives>
            <class>FileLogService</class>
        </alternatives>

    </beans>

    So what we have here:

    1. You still cant have two LogClient instances injected with different implementation classes.

    2. You are only limited to static class alternative, you cant use bean instance as alternative.

    3. It will apply to all injection points that use LogConsole.

    Yes, you made LogClient not to be hardcoded with particular implementation of LogConsole but forced yourself into a trap of limiting you application to use only one implementation of service class.

  • Tsolak Petrosian

    Ok, so you are arguing that there should never be a use case requiring my setup (thats a strong assumption). Here is another real world example with same setup.

    public interface Serializer{
    public void serialize(Object object, Writer witer);
    }

    public class XMLSErializer implements Serializer{
    public void serialize(Object object, Writer witer){..}
    }

    public class JSONSErializer implements Serializer{
    public void serialize(Object object, Writer witer){..}
    }

    public class WSController extends AbstractContoller{
    public Serializer serializer = null;

    public void handleRequest(HttpServletRequest request, HttpServletResponse response){
    String id = request.getParameter(“id”);
    Object entity = //get from DAO
    serializer.serialize( entity, response.getWriter() );
    }

    }

    <bean id="xmlSerializer" class="XMLSerializer"/>

      <bean id="jsonErializer" class="JSONSerializer"/>

      <!– will be accessed like http://yourdomain/xml –>
      <bean id="/xml" class="WSController">
         <property name="serializer" ref="xmlSerializer"/>
      </bean>

      <!– will be accessed like http://yourdomain/json –>
      <bean id="/json" class="WSController">
        <property name="serializer" ref="jsonSerializer"/>
      </bean>

    Here we created two controllers that will render entity queried from DAO as JSON or XML. Your proposition of @Alternative wont work here since the selection of service is done runtime insteadof deployment time.

  • Tsolak Petrosian

    I don’t believe you. If you’re setting up every dependency between every class in XML, which is what you’re arguing for above, then you most certainly *are* going to need to reuse the XML descriptors, since they are simply going to be so long and verbose and full of critical information about the structure of your application.

    I dont understand why I need to reuse XML descriptor? The XML descriptor will just be part of my applications “war” or “ear”.
    If you are arguing that my XML will be so big that I have to separate it into a “jar” used across different application archive than its not done
    that way.
    Nothing forces you to only have one big XML file, you can split your configuration into smaller XML files that will be included with one line into
    main XML file.

    3. It will apply to all injection points that use LogConsole.

    Not true. That is what qualifiers are for. To differentiate, semantically, the different requirements of the various injection points.

    We are going in circle here. @Qualifier limits client code to particular service, but to avoid it you are suggesting to use @Alternative instead of @Qualifier, but not to have all clients use the particular service you are now saying to use @Qualifier that defeats the point of using @Alternative.

  • Tsolak Petrosian

    I will keep my reply short since you finally showed your solution to my use case.

    1. What you just showed whould have been laughed in the old days, because you just misused inheritance and defined 2 new classes just to be able to pass two different object to the class.

    2. You guys are so into this no XML war that everytime someone mentions XML (just for example) you start proving that its bad.

    You really like things to be in Java? Then do this:

    XMLSErializer xmlSerializer = new XMLSErializer();
    JSONSErializer jsonSerializer = new JSONSErializer();

    WSController xmlWSController = new WSController();
    xmlWSController.setSerializer(xmlSerializer);

    WSController jsonWSController = new WSController();
    jsonWSController.setSerializer(jsonSerializer);

    HttpHandler httpHandler = //look up or create
    httpHandler.register(“/xml”, xmlWSController);
    httpHandler.register(“/json”, jsonWSController);

    Its all pure Java no XML, and very simple.
    Its just people tend to make thing complex and come up with framework that supposedly would make it easy.

  • Tsolak Petrosian

    Gavin, I do appreciate you taking time showing me your point of view. I think I should have given example in plain Java code instead of Spring XML to avoid confusion that I am all for XML configuration.

    I do use Spring extensively but thats not for DI but its large library and nice support for Java proxies and AOP. Honestly I see very little use of DI, and I am sure Martin Fowler must be surprised seeing how his idea got such a widespread use.

    In the end I still dont buy your solution (the task was about doing the example wiring without introducing a new class) but let just see how your CDI implementation will stand against the time.

  • Tsolak Petrosian

    Gavin I am puzzled that you dont see basic OO desig being broken with your solutions.
    Do you understand what “separation of concern” in OO means? Its keeping enought information in the object for it to function and remove everything else.

    - First XmlWsController is a badly designed class. Its passed XMLSerialier that it should never care about, since all the methods XmlWsController needs are defined in Serializer interface (basic programing against interfaces). Are you suggesting to extend every bean for every injection points? What about if there are multiple injection points, so we end up with many combinations of classes extending WsController? I am not even mentioned if WsController is a third party class and is defined as final.
    Thats what I mean by introducing a new class. Not a class that will load my wiring (or the XML you are mentioning) but a new class for every injection point of one bean wiring (the more beans are wired the more useless classes you will have).

    - Now @Produces. This works as long as you keep a third Java class that will be injected with HttpHandler and WSController using XML annotation (from producer) and call “register” on HttpHandler with passed WSController.
    If you read my article thats what I was proposing with third party object which in case of Spring is XML and in your case combination of at least one wiring and a Producer Java classes.

  • Tsolak Petrosian

    I was hoping you would see the problem with your “inheritance” example, but you dont want to accept it which is fine for me.

    My article or our conversation was never about Spring vs Seam (or any other CDI implementation) it was all about the issues with most of CDI wiring options and examples on the web and in Seam documentation.

    I never argued about superiority of XML configuration but I am happy you finally showed your non XML solution, which again proves my original point in the article.

    As for Spring vs Seam lets just leave it to user to decide between Spring and Seam like they have decided between Hibernate and plain JDBC programing.

  • Tsolak Petrosian

    Gavin its funny that you are naming people stupid and mentioning computer science but fail to understand this simple rule of inheritance:

    Inheritance is only used for defining a “type”!
    In java you should use inheritance in only 2 cases:

    1. Implementing interface in order to add a type to your class (Polymorphism).
    2. Extend abstract class in order to add a type and some predefined behaviour and maybe code reuse (Polymorphism+templating).

    Extending a class to inject an object does not fall in either category. You use “public” methods to do that!
    XmlWsController is perfect example where inheritance was used instead of composition and even worse the base WsController was already designed to use composition by referencing Serializer throught get/set methods.

    Here is a sample code to show how bad your inheritance approach can be (you seems to understand examples better):

    public interface TranslationService{
    public String translate(String s);
    }

    public class WSTranslationController{

    private TranslationService translationService = null;

    public void handleRequest(HttpServletRequest request, HttpServletResponse response){
    String text = request.getParameter(“text”);
    String translatedText = translationService.translate(text);
    response.getWriter().write(translatedText);
    }
    }

    public class TranslationSwingApp{

    private TranslationService translationService = null;

    public TranslationSwingApp(){
    //setup you swing GUI with TextField and Button to get translation
    }

    public void handleTransletButtonClicj(){
    String text = originalLanguageTextField.getText();
    String translatedText = translationService.translate(text);
    translatedTextField.setText(translatedText);
    }
    }

    public class WicketTranslationWidget{
    //setup all you wicket UI objects

    public void onSubmit(){
    String text = originalLanguageTextField.getText();
    String translatedText = translationService.translate(text);
    translatedTextField.setText(translatedText);
    }

    }

    Lets say that there are 50 TranslationService implementations for each 50 languages.
    With your inheritance example I need to extend 3 classes (WSTranslationController, TranslationSwingApp and WicketTranslationWidget)
    for each TranslationService implementation, in total having 200 classes instead of just having 50 classes + 150 lines of code were
    we call setTranslationService(..) or 150 lines of XML where we do

    <property name="translationService" ref="…"/>

    .

  • Tsolak Petrosian

    There are so much trolling from you that I dont even want to comment on each.

    Anyways here are some references about right way of using inheritance:

    http://www.artima.com/lejava/articles/designprinciples4.html

    http://books.google.com/books?id=ka2VUBqHiWkC&pg=PA81&lpg=PA81&dq=Favor+composition+over+inheritance+bloch&source=bl&ots=yXKlQfu4OY&sig=dOsRJwBlGQhfrKOdEFQ_roEGJX0&hl=en&ei=uUezTOi1IMienAfirZGiBg&sa=X&oi=book_result&ct=result&resnum=2&ved=0CBYQ6AEwAQ#v=onepage&q&f=false

    At least Erich Gamma and Josh Bloch should be enough authority for you. You can google “Favor composition over inheritance” for more.

    And here is a very nice quote from Wikipedia:

    One of the earliest motivations for using inheritance was the reuse of code which already existed in another class. This practice is usually called implementation inheritance.

    In most quarters, class inheritance for the sole purpose of code reuse has fallen out of favor. The primary concern is that implementation inheritance does not provide any assurance of polymorphic substitutability—an instance of the reusing class cannot necessarily be substituted for an instance of the inherited class. An alternative technique, delegation, requires more programming effort but avoids the substitutability issue. In C++ private inheritance can be used as form of implementation inheritance without substitutability. Whereas public inheritance represents an “is-a” relationship and delegation represents a “has-a” relationship, private (and protected) inheritance can be thought of as an “is implemented in terms of” relationship[1].

    I hope you are at least familiar with Martin Fowler work on DI (the guy who actually came up with the definition):

    http://martinfowler.com/articles/injection.html#InversionOfControl

  • Tsolak Petrosian

    Please dont pollute this discussion with your lengthy explanation of substitution. I did not say your example breaks polymorphic substitution (cant even think how in Java you can break it) and please stop commenting on things which I never said (like this case or being pro Spring and XML). The wiki quote was provided to show that implementation inheritance is not favored and instead use of “delegate” is recommended. Its just was mentioning substitution as “primary” reason that not neccesary applicable to our case.

    So your response is three (3) appeals to authority. You’re really not good at this, are you? Obviously you don’t even understand the authorities you’re reading well enough to be able to translate their arguments into your own words, and show how they actually apply to this situation. That’s frankly really disappointing. You should feel a bit betrayed that your expensive education in computing has left you so unprepared to actually discuss technical issues using logical reasoning and technical arguments.

    Thats not a mature behaviour you are showing here. There is no reason to mock about authority here since insteadof me writing the whole chapter for you I am pointing to Bloack’s book as a source, so please give relevant arguments and show what in those articles contradic my comments.
    What I said about inheritance is confirmed by Bloch and others except he mentioned the edge case that is safe to use your example and you jumped on it.

    But lets see what it gives you..

    Here is what Block says about your inheritance case:

    Item 14: Favor composition over inheritance
    Inheritance is a powerful way to achieve code reuse, but it is not always the best tool for the job. Used inappropriately, it leads to fragile software. It is safe to use inheritance within a package, where the subclass and the superclass implementation are under the control of the same programmers. It is also safe to use inheritance when extending classes specifically designed and documented for extension (Item 15). Inheriting from ordinary concrete classes across package boundaries, however, is dangerous. As a reminder, this book uses the word “inheritance” to mean implementation inheritance (when one class extends another).

    Item 15: Design and document for inheritance or else prohibit it
    Item 14 alerted you to the dangers of subclassing a “foreign” class that was not designed and documented for inheritance. So what does it mean for a class to be designed and documented for inheritance? First, the class must document precisely the effects of overriding any method. In other words, the class must document itsself-use of overridable methods: For each public or protected method or constructor, its documentation must indicate which overridable methods it invokes, in what sequence, and how the results of each invocation affect subsequent processing. (By overridable, we mean nonfinal and either public or protected.) More generally, a class must document any circumstances under which it might invoke an overridable method. For example, invocations might come from background threads or static initializers.

    In summary, concrete Class inheritance should not be done against third party classes and needs to be very well documented. This pretty much limits their use to very special case. My original definition says not to use them at all since most developers wont have the need for that special case and will most likely misuse it. But thats not what our discussion is about, so lets continue with your comment on this case:

    Which is the case we have here.

    Did I get it right that you accept the fact that injection via inheritance for third party classes is bad choice?

    I don’t think you’ve mentioned that WsController and JsonSerializer are written and maintained be different teams. It certainly sounds unlikely.

    I am sorry, but I dont have to mention things like this. Java classes can come from different sources, I have never seen any framework putting some type of precondition on this.
    None is going to ask that either before choosing your solution and most certainly will try to use third party library.

  • wangliyu

    Is this the new war between CDI(Guice) and Spring? Can’t believe there is still a Spring die-hard out there. Even Spring3 support annotation (even it’s ugly), The only problem I have with Spring is that Spring guys try to anything they can to avoid standard (I mean JCP spec), so if you are on the boat, you sink with the boat, before it sunk, they won’t tell you anything about it, I was using Spring before Mr. Johnson switching license back and force.

    Anyway, I think CDI is more about “Just DI”, it does provide context (session, conversation and can add customized context), but Spring is suck at here, Spring support AspectJ annotation style (which I really hate, if it possible, just use AspectJ lang), Spring3 has SPEL(WTF name), it’s seems EL + Java binary code, wierd to me, I would prefer just write Java code or I’ll write C.

    BTW, XML isn’t OO, and I wondering where you get educated other than “Spring University”?

    • Tsolak Petrosian

      BTW, XML isn’t OO, and I wondering where you get educated other than “Spring University”?

      Thats funny. Hopefully you read the article insteadof just seeing “XML” and “OO” and jumping on to comments.

      But your post is good example of how extreme most developers can be, and what separates good software engineer from the rest.

      Lets just look back into history:

      1. First it was just Java code and plain “main” classes.

      2. Then EJB and XML bandwagon came and almost everyone jumped on it and companies started rewriting their systems using EJB’s and doing XML from remote communication to application configurations.

      3. Then POJO’s came to light with frameworks like Spring and Hibernate, both still using XML configuration. And again most switched from EJB to Spring+Hibernate + XML. All yelling that EJB container does not buy you anything and is not cool anymore.

      4. Then annotation came to light and everyone assumed its Java code and is now in progress of moving so called XML hell into Java annotation hell, yelling that it was good idea to do everything in Java after all. Funny thing is that ironically this is still called EJB.

      Now most are back to square one, and worst of all they brought the annotation bloat along with them and I am not sure how long it will take for them to see it.

      Smart developers on other hand never chase latest and greatest but pick and choose what is best for their current needs. They evaluate and question the new technology (like its done in this article) before jumping on it. And again they don’t just read “XML” and “OO” and say “XML isn’t OO” or see “Annotation” and “plain Java” and assuming “will just write Java code”.

      BTW,

      The only problem I have with Spring is that Spring guys try to anything they can to avoid standard (I mean JCP spec), so if you are on the boat, you sink with the boat, before it sunk, they won’t tell you anything about it, I was using Spring before Mr. Johnson switching license back and force.

      JCP has never been anything worthy, and has become almost nothing, Dough Lea’s departure is another example.
      The only standard you will have from now on is Oracle’s standard.

  • Roy Nid

    Come on guys! This is just getting interesting…love the awesome flame war! This is waaaaaay better than what’s on TV tonight!

Leave a Reply

  

  

  


9 × two =

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>