简体   繁体   中英

Java configuration and dependency injection (akin to Springs IoC vs. Weld / Guice)

Lets say I have a class ListCreator which I want to configure. I want to be able to tell it the sort order and the how to output my table. Therefore I have the boolean sortDescending property and the TableWriter interface which is implemented by PdfTableWriter (but also by XlsTableWriter ). In this example I think configuration and DI go hand in hand. I would like to write something like this Spring (pseudo) example:

<beans>
    <bean id="ListCreator" class="ModularListCreator">
        <property name="tableWriter">
            <ref local="TableWriter"/>
        </property>
        <property name="sortDescending">
            <value>true</value>
        </property>
    </bean>
    <bean id="TableWriter" class="PdfTableWriter"> </bean>
</beans>

Now Spring can do this, but it seems like Weld & Guice can not. Weld for example lets you choose alternatives in the beans.xml, but only for the whole application. What if I want to have one ListCreator for PDFs and another one for XLS at the same time?

I do not get the scope of Weld and Guice at the moment, as they don't seem to allow much of a configuration. The seem to only alleviate the need to write new or to implement your own factories. EJB injection does the same for example, which is nice, but where is the whole configuration part (choosing which instance with what parameters I actually want where).

Coming to the real point: I do not want to use Spring as it seems to be overhead. I much rather use something clean and small at best specified by a JSR. Any suggestions?

InPUT offers a way if you want a flexible description-based solution. I created the whole example, and added it to the example section .

The code is minimal with:

Design config = new Design("config.xml");
ListCreator creator = config.getValue("ListCreator");

assuming you have a config.xml InPUT design which contains the settings in InPUT syntax:

<SValue id="ListCreator">
    <NValue id="SortDescending" value="false"/>
    <SValue id="TableWriter" value="Xls"/>
</SValue>

For this to work, you have to define the design space as follows:

<SParam id="ListCreator">
    <NParam id="SortDescending" type="boolean" />
    <SParam id="TableWriter">
        <SChoice id="Xls"/>
        <SChoice id="Pdf"/>
    </SParam>
</SParam>

You now tailor the programming language independent design space to your Java implementation in a code mapping :

<Mapping id="ListCreator" type="test.ListCreator" constructor="TableWriter SortDescending"/>
<Mapping id="ListCreator.TableWriter" type="test.TableWriter"/>
<Mapping id="ListCreator.TableWriter.Xls" type="test.XlsTableWriter"/>
<Mapping id="ListCreator.TableWriter.Pdf" type="test.PdfTableWriter"/>

From here, extend and customize at free will without touching the code. You mention the case with multiple ListCreator instances. You would have to make 3 changes:

1) design space:

<SValue id="ListCreator" type="[]">

2) design (for example):

<SValue id="ListCreator">
    <SValue id="1">
        <NValue id="SortDescending" value="true"/>
        <SValue id="TableWriter" value="Pdf"/>
    </SValue>
    <SValue id="2">
        <NValue id="SortDescending" value="false"/>
        <SValue id="TableWriter" value="Xls"/>
    </SValue>
</SValue>

3) Be prepared to receive an array instead (code):

ListCreator[] creators = config.getValue("ListCreator");

You decide the number and alternatives in the descriptor; the entries arrive in the defined order. This works similar for multiple dimensions (eg "[][][]"). You can add alternative table writers with further parameters in the future or change the current parameters without code changes on the caller side. Just make sure the classes are all available, and to test it. There are some sources of error (typos).

Guice actually gives you a lot of power for configuration. Assuming I'm understanding you correctly, here's a simple example of one way you could do this in Guice, using a provider method in a module.

protected void configure() {
  bind(TableWriter.class).to(PdfTableWriter.class);
}

@Provides
protected ListCreator provideListCreator(TableWriter tableWriter) {
  ModularListCreator result = new ModularListCreator();
  result.setTableWriter(tableWriter);
  result.setSortDescending(true);
  return result;
}

There are other ways too, including making setSortDescending use a binding annotation:

@Inject public void setSortDescending(
    @Named("sortDescending") boolean sortDescending)

and then binding that property:

protected void configure() {
  bind(TableWriter.class).to(PdfTableWriter.class);
  bindConstant().annotatedWith(Names.named("sortDescending")).to(true);
  bind(ListCreator.class).to(ModularListCreator.class);
}

For CDI, checkout Seam Solder. It adds the ability to easily configure Managed Beans from an xml file. Judging by the close relationship between the Seam and Weld teams, this mechanism probably has a good chance of making it into a future JSR.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM