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.

10 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

Leave a Reply

  

  

  


seven + = 9

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>