Print
Type Based Post Instantiation

Problem

In Post Instantiation with AspectJ, we presented a solution that uses an "Injection" interface to encapsulate the injection logic described in the xml file. This enables the domain object to be pojo without any dependency to the container.

Yet, there is an awkward problem: we need an aspect defined for each different domain object type. This is necesssary for us to appy different injection logic based on the domain object type.

Though the aspect is trivial, it is still painful to write them down for our one thousand and fourty seven domain object types.

In Annotation Based Auto Wiring, we gave a solution that uses autowiring and annotation to eliminate the need for such trivial aspects. Nice as it is, it is however possible that autowiring doesn't work, or annotation can't be used.

In this article, we'll give a more comprehensive solution that address the problem completely.

Solution

We will only need to write one (or, a few more maybe) aspect:

import jfun.yan.etc.injection.Injection;

public abstract aspect BaseInjectionAspect {
  private Injection injection;

  public void setInjection(Injection inj){
    this.injection = inj;
  }

  /**
   * the creation of any object that is a client of the 
   * Person.new service
   */
  abstract pointcut clientCreation(Object aClient);

  /**
   * inject clients when they are created
   */
  after(Object aClient) returning :
    clientCreation(aClient) {
    injection.inject(aClient);
  }
}
public aspect DomainInjectionAspect extends BaseInjectionAspect{

  /**
   * the creation of any object that is a client of the 
   * Person.new service
   */
  pointcut clientCreation(Object aClient) :
    initialization(com.mycompany.domainmodels.*+.new(..)) &&
    this(aClient);

}

Now, suppose we have several domain object types: Person, Fruit, Apple and Orange, where Apple and Orange are subclasses of Fruit.

Our configuration file will be look like:

<module name="test aspectj">
<nut name="injector" class="jfun.yan.xml.nuts.optional.InjectorAspectNut"/>
<!-- load the typecase tag -->
<nut name="typecase" class="jfun.yan.xml.nuts.optional.InjectionTypeCaseNut"/>
<body>
  <bean id="service1" .../>
  <bean id="service2" .../>
  
  <!-- ALL SORTS OF INJECTION LOGIC START HERE -->
  <function id="inject person" params="person">
    <bean component="$person" props="{service1=$service1}"/>
  </function>
  <function id="inject fruit" params="fruit">
    <bean component="$fruit">
      <prop key="name">
        <getter component="$fruit" name="class.name"/>
      </prop>
      <prop key="service1" val="$service1"/>
    </bean>
  </function>
  <function id="inject orange" params="orange">
    <bean component="$orange"
        props="{name=orange,service1=$service1,service2=$service2}"/>
  </function>
  <!-- ALL SORTS OF INJECTION LOGIC END HERE -->
  
  <!-- use typecase to glue them together -->
  <typecase id="the_injection">
    <case type="com.mycompany.domainmodels.Orange" injection="$inject orange"/>
    <case type="com.mycompany.domainmodels.Fruit" injection="$inject fruit"/>
    <case type="com.mycompany.domainmodels.Person" injection="$inject person"/>
  </typecase>
  
  <!-- use the type-case'd injection to configure the generic aspect -->
  <injector 
      class="com.mycompany.DomainInjectionAspect" injection="$the_injection"/>
</body>
</module>
  • Person needs a Service1 to be injected;
  • Fruit needs both a name and Service1 to be injected;
  • Orange as a sub type of Fruit, has a special need for acidity and name, so we override it;
  • Apple is a Fruit and has no special need, so we just use the injection logic of Fruit.

In this configuration file, we use "inject orange", "inject fruit", "inject person" to describe different injection logic for different types.

We then use the <typecase> tag implemented by InjectionTypeCaseNut class to add the "typecase" logic to the final Injection object.

Finally, the <injector> tag is used to configure the DomainInjectionAspect.

Summary

In this solution:

  1. annotation can still be used to help auto-wiring, but not required. The domain object can remain plain plain pojo.
  2. one-aspect-per-type is avoided.
  3. sub-type relationship works. Apple can be injected as a Fruit.
Powered by Atlassian Confluence