Articles

Dependency injection with Xml and Yaml in the Ding container


Introduction

In this past article I've discussed the dependency injection features when using annotations. This time, we'll see how to use the xml and yaml drivers to do the same (setter and constructor injection). If you don't know how to configure the xml and yaml drivers, please start by reading this.

In the ding manual you can find extensive examples (almost all with xml and annotations). You are encouraged to take a look at it. This article will reproduce some of the examples there, extending the concepts a little bit, and giving some missing-from-the-manual YAML examples.

Also, the manual explains other stuff not mentioned here, like bean aliasing and inheritance, hooking in the bean lifecycle, aspect oriented programming (explained here), factory beans and factory classes, etc.

And, you can see the full examples for xml and yaml at the Ding github repository.

Setter Injection

Setter injection works given 2 conditions:

  • You declare "properties" in your bean definitions.
  • You include the needed setter methods in your classes.

Quick example: You have a repository class, and you want to set a value when you create an instance of it. Let's say you want to set the DB driver for a given repository, you could do something like:

$repository->setDriver($mysqlDriver);

In this case, you would define a "driver" property. And ding will automatically look for a "setDriver" method and use it to inject the configured values. The convention is that the property name is upercamel cased first, and then prepended with the word set. The new string is the name of the method to be called to perform the setter injection. That's it. Let's now see how to actually do this :)

Injecting scalar variables

The general syntax for setter injection with xml is the following:

<beans>
  <bean id="myBeanName" class="MyBean">
    <property name="someProperty">
      <value>aLiteralValue</value>
    </property>
  </bean>
</beans>

Equivalent with YAML:

beans: !!map
  myBeanName:
    class: MyBean
    properties: !!map
      someProperty:
        value: aLiteralValue

You can also insert some special values, like "true", "false", or "null":

<beans>
  <bean id="myBeanName" class="MyBean">
    <property name="someNullProperty">
      <null/>
    </property>
    <property name="someTrueProperty">
      <true/>
    </property>
    <property name="someFalseProperty">
      <false/>
    </property>
  </bean>
</beans>

In YAML:

beans: !!map
  myBeanName:
    class: MyBean
    properties: !!map
      someNullProperty:
        value: null
      someTrueProperty:
        value: true
      someFalseProperty:
        value: false

Injecting php code

Of course, doing this is not recommended. But if you really need to, you can execute code and have the result injected as a property in your beans:

<beans>
  <bean id="myBeanName" class="MyBean">
    <property name="someProperty">
      <eval><![CDATA[ return "Some evaluated code"; ]]></eval>
    </property>
  </bean>
</beans>

In YAML:

beans: !!map
  myBeanName:
    class: MyBean
    properties: !!map
      someProperty:
        eval: return "Some evaluated code";

Injecting user properties

You can also inject property values. In this case, we are injecting a property registered in the container or the properties holder. In this case, the container will inject the value of the property "user.name":

<beans>
  <bean id="myBeanName" class="MyBean">
    <property name="someProperty">
      <value>${user.name}</value>
    </property>
  </bean>
</beans>

In YAML:

beans: !!map
  myBeanName:
    class: MyBean
    properties: !!map
      someProperty:
        value: ${user.name}

Injecting references to other beans (collaborators)

The usual case is the need to have a bean collaborating with another one. This translates into a bean having a dependency on another bean (the collaborator). To achieve this:

<beans>
  <bean id="myOtherBeanName" class="MyCollaborator"/>
  <bean id="myBeanName" class="MyBean">
    <property name="someProperty">
      <ref bean="myOtherBeanName"/>
    </property>
  </bean>
</beans>

In YAML:

beans: !!map
  myOtherBeanName:
    class: MyCollaborator
  myBeanName:
    class: MyBean
    properties: !!map
      someProperty:
        ref: myOtherBeanName

You can also define a collaborator in the actual property where it will be injected, instead of defining the collaborator globally. This is useful to cleanup your xml/yaml files. These are called inner beans, which also, can be anonymous (i.e: without a given name):

<beans>
  <bean id="myBeanName" class="MyBean">
    <property name="someProperty">
      <bean class="MyCollaborator"/>
    </property>
  </bean>
</beans>

In YAML:

beans: !!map
  myBeanName:
    class: MyBean
    properties: !!map
      someProperty:
        bean:
          class: MyCollaborator

Being a bean definition themselves, you can have setter and constructor injection as well, and every other feature available when defining beans.

Injecting arrays

You can also inject arrays with values, specifying an optional key for each entry. In the following example, an associative array is injected, with several different values. Note how you can combine the syntax we saw above (remember, the array keys are completely optional):

<beans>
  <bean id="myBeanName" class="MyBean">
    <property name="someProperty">
      <entry key="key1">
        <value>aLiteralValue</value>
      </entry>
      <entry key="key2">
        <value>${user.name}</value>
      </entry>
      <entry key="key3">
        <bean class="MyCollaborator"/>
      </entry>
      <entry key="key4">
        <ref bean="otherBean"/>
      </entry>
      <entry key="key5">
        <eval><![CDATA[ return "Some evaluated code"; ]]></eval>
      </entry>
      <entry key="key6">
        <array>
          <entry>
            <value>${aValue}</value>
          </entry>
        </array>
      </entry>
    </property>
</beans>

In YAML:

beans: !!map
  myBeanName:
    class: MyBean
    properties: !!map
      someProperty:
        value: !!map
          key1:
            value: aLiteralValue
          key2:
            value: ${user.name}
          key3:
            bean:
              class: MyCollaborator
          key4:
            ref: otherBean
          key5:
            eval: return "Some evaluated code";
          key6:
            value: !!map
              key:
                value: ${aValue}

Constructor Injection

Constructor injection is pretty much equal to setter injection. The syntax for declaring constructor arguments is a little different, but you can use the same syntax to specify the values for the constructors. The constructor arguments are evaluated IN ORDER. But you can optionally specify the "name" of the argument

<beans>
  <bean id="myBeanName" class="MyBean">
    <constructor-arg>
      <value>aLiteralValue</value>
    </constructor-arg>
  </bean>
</beans>

Specifying constructor argument names:

<beans>
  <bean id="myBeanName" class="MyBean">
    <constructor-arg name="argument1">
      <value>aLiteralValue</value>
    </constructor-arg>
  </bean>
</beans>

In YAML you would do:

beans: !!map
  myBeanName:
    class: MyBean
    constructor-args:
      - aLiteralValue
beans: !!map
  myBeanName:
    class: MyBean
    constructor-args:
      argument1: aLiteralValue

Constructors can be injected with arrays, user properties, inner beans, etc. All what we saw for setters (properties) also apply to constructors, so I wont go over it. Just try the same syntax as with setter injection :)

Method Injection

Method injection allows you to override a method in a given bean. In this case, the method "createDependency" in the bean "myBeanName" will be overriden (by the container) with an implementation that returns the bean "dependencyName".

<beans>
  <bean id="dependencyName" class="MyOtherBean" scope="prototype"/>
  <bean id="myBeanName" class="MyBean">
    <lookup-method name="createDependency" bean="dependencyName"/>
  </bean>
</beans>

In YAML:

beans: !!map
  myBeanName:
    class: MyBean
    lookup-methods: !!map
      createDependency: dependencyName

This is a nice feature, very useful in certain ocasions. See this post.

Conclusions

So you've seen an overview of how to make ding completely assemble your application components without using annotations. But, XML and YAML can get quite verbose, that's why I personally preffer to use annotations. But, xml and yaml can be really handy sometimes, and actually complements the annotations. Besides that, they provide a non-invasive way of configuring our dependencies, so that's cool to have, too.