Using Method Injection

A new IoC-oriented feature introduced with Spring 1.1 is Method Injection, which allows greater flexibility for interactions between collaborators. Spring's Method Injection capabilities come in two loosely related forms, Lookup Method Injection and Method Replacement. Lookup Method Injection provides a new mechanism by which a bean can obtain one of its dependencies, and Method Replacement allows you to replace the implementation of any method on a bean arbitrarily, without having to change the original source code.

In order to provide these two features, Spring uses the dynamic bytecode enhancement capabilities of CGLIB. If you want to use Lookup Method Injection or Method Replacement in your application, make sure you have the CGLIB JAR file on your classpath. Since version 1.1, Spring has included a fully packaged CGLIB JAR file, which includes the ASM bytecode manipulation library as well.

Lookup Method Injection

Lookup Method Injection was added to Spring to overcome the problems encountered when a bean depends on another bean with a different lifecycle—specifically, when a singleton depends on a non-singleton. In this situation, both setter and constructor injection result in the singleton maintaining a single instance of what should be a non-singleton bean. In some cases, you will want to have the singleton bean obtain a new instance of the non-singleton every time it requires the bean in question.

Typically, you can achieve this by having the singleton bean implement BeanFactoryAware. Then, using the BeanFactory instance, the singleton bean can look up a new instance of the non-singleton dependency every time it needs it. Lookup Method Injection allows the singleton bean to declare that it requires a non-singleton dependency and receive a new instance of the non-singleton bean each time it needs to interact with it, without needing to implement any Spring-specific interfaces.

Lookup Method Injection works by having your singleton declare a method, the lookup method, which returns an instance of the non-singleton bean. When you obtain a reference to the singleton in your application, you are actually receiving a reference to a dynamically created subclass on which Spring has implemented the lookup method. A typical implementation involves defining the lookup method, and thus the bean class, as abstract. This prevents any strange errors from creeping in when you forget to configure the Method Injection and you are working directly against the bean class with the empty method implementation instead of the Spring-enhanced subclass. This topic is quite complex and is best shown by example.

In this example, we create one non-singleton bean and two singleton beans that both implement the same interface. One of the singletons obtains an instance of the non-singleton bean using "traditional" setter injection; the other uses Method Injection. Listing 5-17 shows the MyHelper bean, which in our example is the non-singleton bean.

Listing 5-17: The MyHelper Bean

image from book
package com.apress.prospring.ch5.mi;      public class MyHelper {          public void doSomethingHelpful() {         // do something!     } }
image from book

This bean is decidedly unexciting, but it serves the purposes of this example perfectly. In Listing 5-18, you can see the DemoBean interface, which is implemented by both of the singleton beans.

Listing 5-18: The DemoBean Interface

image from book
package com.apress.prospring.ch5.mi;      public interface DemoBean {          public MyHelper getMyHelper();     public void someOperation(); }
image from book

This bean has two methods: getMyHelper() and someOperation(). The sample application uses the getMyHelper() method to get a reference to the MyHelper instance and, in the case of the method lookup bean, to perform the actual method lookup. The someOperation() method is a simple method that depends on the MyHelper class to do its processing.

Listing 5-19 shows the StandardLookupDemoBean class, which uses setter injection to obtain an instance of the MyHelper class.

Listing 5-19: The StandardLookupDemoBean Class

image from book
package com.apress.prospring.ch5.mi;      public class StandardLookupDemoBean implements DemoBean {          private MyHelper helper;          public void setMyHelper(MyHelper helper) {         this.helper = helper;     }          public MyHelper getMyHelper() {         return this.helper;     }          public void someOperation() {         helper.doSomethingHelpful();     } }
image from book

This code should all look familiar, but notice that the someOperation() method uses the stored instance of MyHelper to complete its processing. In Listing 5-20, you can see the AbstractLookupDemoBean class, which uses Method Injection to obtain an instance of the MyHelper class.

Listing 5-20: The AbstractLookupDemoBean Class

image from book
package com.apress.prospring.ch5.mi;      public abstract class AbstractLookupDemoBean implements DemoBean {          public abstract MyHelper getMyHelper();          public void someOperation() {         getMyHelper().doSomethingHelpful();     } }
image from book

Notice that the getMyHelper() method is declared as abstract, and that this method is called by the someOperation() method to obtain a MyHelper instance. In Listing 5-21, you can see the configuration code required for this example.

Listing 5-21: Configuring Method Lookup Injection

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean              singleton="false"/>     <bean             >         <lookup-method name="getMyHelper" bean="helper"/>     </bean>     <bean             >         <property name="myHelper">             <ref local="helper"/>         </property>     </bean> </beans>
image from book

The configuration for the helper and standardLookupBean beans should look familiar to you by now. For the abstractLookupBean, you need to configure the lookup method using the <lookup-method> tag. The name attribute of the <lookup-method> tag tells Spring the name of the method on the bean that it should override. This method must not accept any arguments and the return type should be that of the bean you want to return from the method. The bean attribute tells Spring which bean the lookup method should return.

The final piece of code for this example is shown in Listing 5-22.

Listing 5-22: The LookupDemo Class

image from book
package com.apress.prospring.ch5.mi;      import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource; import org.springframework.util.StopWatch;      public class LookupDemo {          public static void main(String[] args) {         BeanFactory factory = new XmlBeanFactory(new FileSystemResource(                 "./ch5/src/conf/mi/lookup.xml"));              DemoBean abstractBean = (DemoBean) factory.getBean("abstractLookupBean");         DemoBean standardBean = (DemoBean) factory.getBean("standardLookupBean");              displayInfo(standardBean);         displayInfo(abstractBean);          }          public static void displayInfo(DemoBean bean) {         MyHelper helper1 = bean.getMyHelper();         MyHelper helper2 = bean.getMyHelper();              System.out.println("Helper Instances the Same?: "                 + (helper1 == helper2));              StopWatch stopWatch = new StopWatch();         stopWatch.start("lookupDemo");              for (int x = 0; x < 100000; x++) {             MyHelper helper = bean.getMyHelper();             helper.doSomethingHelpful();         }              stopWatch.stop();              System.out.println("100000 gets took " + stopWatch.getTotalTimeMillis()                 + " ms");          } }
image from book

In this code, you can see that we retrieve the abstractLookupBean and the standardLookupBean from the BeanFactory and pass each reference to the displayInfo() method. The first part of the displayInfo() method creates two local variables of MyHelper and assigns them each a value by calling getMyHelper() on the bean passed to it. Using these two variables, it writes a message to stdout indicating whether or not the two references point to the same object. For the abstractLookupBean class, a new instance of MyHelper should be retrieved for each call to getMyHelper(), so the references should not be the same. For standardLookupBean, a single instance of MyHelper is passed to the bean by setter injection, and this instance is stored and returned for every call to getMyHelper(), so the two references should be the same.

Note 

The StopWatch class used in the previous example is a utility class available with Spring. You'll find StopWatch very useful when you need to perform simple performance tests and when you are testing your applications.

The final part of the displayInfo() method runs a simple performance test to see which of the beans is faster. Clearly the standardLookupBean should be faster because it returns the same instance each time, but it is interesting to see the difference. Here is the output we received from this example:

Helper Instances the Same?: true 100000 gets took 16 ms Helper Instances the Same?: false 100000 gets took 2063 ms

As you can see, the helper instances are, as expected, the same when we use standardLookupBean and different when we use abstractLookupBean. There is a noticeable performance difference when we use the standardLookupBean, but that is to be expected.

Considerations for Method Lookup Injection

Method Lookup Injection is intended for use when you want to work with two beans of different lifecycles. Avoid the temptation to use Method Lookup Injection when the beans share the same lifecycle, especially if they are singletons. Listing 5-22 shows a noticeable difference in performance between using Method Injection to obtain new instances of a dependency and using standard DI to obtain a single instance of a dependency. Also, make sure you don't use Method Lookup Injection needlessly, even when you have beans of different lifecycles.

Consider a situation in which you have three singletons that share a dependency in common. You want each singleton to have its own instance of the dependency, so you create the dependency as a non-singleton, but you are happy with each singleton using the same instance of the collaborator throughout its life. In this case, setter injection is the ideal solution; Method Lookup Injection just adds unnecessary overhead.

As we mentioned before, Method Lookup Injection was created so you could avoid having lots of beans that implemented BeanFactoryAware and performed lookups manually using getBean(). So how does performing manual lookups with BeanFactoryAware compare to Method Lookup Injection from a performance perspective? Listing 5-23 shows another implementation of the DemoBean interface that also implements BeanFactoryAware and uses the getBean() method to look up its non-singleton dependency manually.

Listing 5-23: The BeanFactoryAwareLookupDemoBean Class

image from book
package com.apress.prospring.ch5.mi;      import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware;      public class BeanFactoryAwareLookupDemoBean implements BeanFactoryAware,         DemoBean {          private BeanFactory factory = null;          public void setBeanFactory(BeanFactory factory) throws BeansException {         this.factory = factory;     }          public MyHelper getMyHelper() {         return (MyHelper) factory.getBean("helper");     }          public void someOperation() {         getMyHelper().doSomethingHelpful();     } }
image from book

As you can see, the getMyHelper() performs a lookup each time it is called, returning a new instance of the helper bean each time. In Listing 5-24 you can see a simple performance test that compares this class with the AbstractLookupDemoBean class that uses Method Lookup Injection.

Listing 5-24: Comparing Performance

image from book
package com.apress.prospring.ch5.mi;      import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource; import org.springframework.util.StopWatch;      public class LookupPerformance {          public static void main(String[] args) {              BeanFactory factory = new XmlBeanFactory(new FileSystemResource(                 "./ch5/src/conf/mi/lookup.xml"));              DemoBean abstractBean = (DemoBean) factory.getBean("abstractLookupBean");         DemoBean factoryBean = (DemoBean) factory.getBean("factoryLookupBean");              testPerf(abstractBean);         testPerf(factoryBean);     }          public static void testPerf(DemoBean bean) {         StopWatch stopWatch = new StopWatch();         stopWatch.start("perfTest");         for (int x = 0; x < 1000000; x++) {             MyHelper helper = bean.getMyHelper();             helper.doSomethingHelpful();         }              stopWatch.stop();              System.out.println("1000000 gets took "                 + stopWatch.getTotalTimeSeconds() + " seconds");     } }
image from book

When we ran this example on our machine, we received the following output:

1000000 gets took 17.563 seconds 1000000 gets took 17.375 seconds

As you can see, there is barely any difference in the performance of the two different mechanisms; the manual lookup approach is marginally faster. Given that the difference in performance is negligible, we recommend that you use Method Lookup Injection when you are working with beans of different lifecycles rather than implementing BeanFactoryAware and performing the lookup manually. Using Method Lookup Injection, you can keep your beans decoupled from Spring without any noticeable performance loss.

When you are using Method Lookup Injection, there are a few design guidelines that you should bear in mind when building your classes. In the earlier examples, we declared the lookup method in an interface. The only reason we did this was we did not have to duplicate the displayInfo() method twice for two different bean types. As we mentioned earlier, generally you do not need to pollute a business interface with unnecessary definitions that are used solely for IoC purposes. Another point to bear in mind is that although you don't have to make your lookup method abstract, doing so prevents you from forgetting to configure the lookup method and then using a blank implementation by accident.

Method Replacement

Although the Spring documentation classifies method replacement as a form of injection, it is very different from what you have seen so far. So far, we have used injection purely to supply beans with their collaborators. Using method replacement, you can replace the implementation of any method on any beans arbitrarily without having to change the source of the bean you are modifying.

Internally you achieve this by creating a subclass of the bean class dynamically. You use CGLIB and redirect calls to the method you want to replace to another bean that implements the MethodReplacer interface.

In Listing 5-25 you can see a simple bean that declares two overloads of a formatMessage() method.

Listing 5-25: The ReplacementTarget Class

image from book
package com.apress.prospring.ch5.mi;      public class ReplacementTarget {          public String formatMessage(String msg) {         return "<h1>" + msg + "</h1>";     }          public String formatMessage(Object msg) {         return "<h1>" + msg + "</h1>";     } }
image from book

You can replace any of the methods on the ReplacementTarget class using Spring's method replacement functionality. In this example, we show you how to replace the formatMessage(String method) and we also compare the performance of the replaced method with that of the original.

To replace a method, you first need to create an implementation of the MethodReplacer class; this is shown in Listing 5-26.

Listing 5-26: Implementing MethodReplacer

image from book
package com.apress.prospring.ch5.mi;      import java.lang.reflect.Method;      import org.springframework.beans.factory.support.MethodReplacer;      public class FormatMessageReplacer implements MethodReplacer {          public Object reimplement(Object target, Method method, Object[] args)             throws Throwable {              if (isFormatMessageMethod(method)) {                  String msg = (String) args[0];                  return "<h2>" + msg + "</h2>";         } else {             throw new IllegalArgumentException("Unable to reimplement method "                     + method.getName());         }     }          private boolean isFormatMessageMethod(Method method) {              // check correct number of params         if (method.getParameterTypes().length != 1) {             return false;         }              // check method name         if (!("formatMessage".equals(method.getName()))) {             return false;         }              // check return type         if (method.getReturnType() != String.class) {             return false;         }              // check parameter type is correct         if (method.getParameterTypes()[0] != String.class) {             return false;         }              return true;     }      }
image from book

The MethodReplacer interface has a single method, reimplement(), that you must implement. Three arguments are passed to reimplement(): the bean on which the original method was invoked, a Method instance that represents the method that is being overridden, and the array of arguments passed to the method. The reimplement() method should return the result of your reimplemented logic and, obviously, the type of the return value should be compatible with the return type of the method you are replacing. In Listing 5-27, the FormatMessageReplacer first checks to see if the method that is being overridden is the formatMessage(String) method; if so, it executes the replacement logic—in this case, surrounding the message with <h2> and </h2>—and returns the formatted message to the caller. It is not necessary to check to see if the message is correct, but this can be useful if you are using a few MethodReplacers with similar arguments. Using a check helps prevent a situation where a different MethodReplacer with compatible arguments and return types is used accidentally.

Listing 5-27 shows a BeanFactory that defines two beans of type ReplacementTarget—one has the formatMessage(String) method replaced and the other does not.

Listing 5-27: Configuring Method Replacement

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             />     <bean             >         <replaced-method name="formatMessage" replacer="methodReplacer">             <arg-type>String</arg-type>         </replaced-method>     </bean>     <bean             /> </beans>
image from book

As you can see from Listing 5-27, the MethodReplacer implementation is declared as a bean in the BeanFactory. We then used the <replaced-method> tag to replace the formatMessage(String) method on the replacementTargetBean. The name attribute of the <replaced-method> tag specifies the name of the method to replace and the replacer attribute is used to specify the name of the MethodReplacer bean that we want to replace the method implementation. In cases where there are overloaded methods such as in the ReplacementTarget class, you can use the <arg-type> tag to specify the method signature to match. The <arg-type> supports pattern matching, so String is matched to java.lang.String and also to java.lang.StringBuffer.

Listing 5-28 shows a simple demo application that retrieves both the standardTarget and replacementTarget beans from the BeanFactory, executes their formatMessage(String) methods, and then runs a simple performance test to see which is faster.

Listing 5-28: Method Replacement in Action

image from book
package com.apress.prospring.ch5.mi;      import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource; import org.springframework.util.StopWatch;      public class MethodReplacementExample {          public static void main(String[] args) {         BeanFactory factory = new XmlBeanFactory(new FileSystemResource(                 "./ch5/src/conf/mi/replacement.xml"));              ReplacementTarget replacementTarget =                            (ReplacementTarget) factory.getBean("replacementTarget");         ReplacementTarget standardTarget =                               (ReplacementTarget) factory.getBean("standardTarget");              displayInfo(replacementTarget);         displayInfo(standardTarget);     }          private static void displayInfo(ReplacementTarget target) {         System.out.println(target.formatMessage("Hello World!"));              StopWatch stopWatch = new StopWatch();         stopWatch.start("perfTest");              for (int x = 0; x < 1000000; x++) {             String out = target.formatMessage("foo");         }              stopWatch.stop();              System.out.println("1000000 invocations took: "                 + stopWatch.getTotalTimeMillis() + " ms");     } }
image from book

You should be very familiar with this code by now, so we won't go into any detail on it. On our machine, running this example yields the following output:

<h2>Hello World!</h2> 1000000 invocations took: 3609 ms <h1>Hello World!</h1> 1000000 invocations took: 844 ms

As expected, the output from the replacementTarget bean reflects the overridden implementation the MethodReplacer provides. Interestingly, though, the dynamically replaced method is over four times slower than the statically defined method. Removing the check for a valid method in the MethodReplacer made a negligible difference (between 20 and 150 milliseconds) across a number of executions, so we can conclude that most of the overhead is in the CGLIB subclass.

When to Use Method Replacement

We will resist the temptation to say "Never." and instead say that method replacement can prove quite useful in a variety of circumstances, especially when you only want to override a particular method for a single bean rather than all beans of the same type. That said, we still prefer to use standard Java mechanisms for overriding methods rather than depending on runtime bytecode enhancement.

If you are going to use method replacement as part of your application, we recommend that you use one MethodReplacer per method or group of overloaded methods. Avoid the temptation to use a single MethodReplacer for lots of unrelated methods; this results in lots of unnecessary String comparisons while your code works out which method it is supposed to reimplement. We have found that performing simple checks to ensure that the MethodReplacer is working with the correct method is useful and doesn't add too much overhead to your code. If you are really concerned about performance, you can simply add a boolean property to your MethodReplacer, which allows you to turn the check on and off using Dependency Injection.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net