Using FactoryBeans

One of the problems that you will face when using Spring is how to create and then inject dependencies that cannot be created simply by using the new operator. To overcome this problem, Spring provides the FactoryBean interface that acts as an adaptor for objects that cannot be created and managed using the standard Spring semantics. Typically, you use FactoryBeans to create beans you cannot use the new operator to create such as those you access through static factory methods, although this is not always the case. Simply put, a FactoryBean is a bean that acts as a factory for other beans. FactoryBeans are configured within your BeanFactory like any normal bean, but when Spring uses the FactoryBean to satisfy a dependency or lookup request, it does not return the FactoryBean; instead, it invokes the FactoryBean.getObject() method and returns the result of that invocation.

FactoryBeans are used to great effect in Spring; the most noticeable uses are the creation of transactional proxies, which we cover in Chapter 13. However, FactoryBeans are not just useful for building the internals of Spring; you'll find them really useful when you build your own applications, because they allow you to manage many more resources using IoC than would otherwise be available.

The MessageDigestFactoryBean

Often the projects that we work on require some kind of cryptographic processing; typically, this involves generating a message digest or hash of a user's password to be stored in a database. In Java, the MessageDigest class provides functionality for creating a digest of any arbitrary data. MessageDigest itself is abstract, and you obtain concrete implementations by calling MessageDigest.getInstance() and passing in the name of the digest algorithm you want to use. For instance, if we want to use the MD5 algorithm to create a digest, we use the following code to create the MessageDigest instance:

MessageDigest md5 = MessageDigest.getInstance("MD5");

If we want to use Spring to manage the creation of the MessageDigest object, the best we can do without a FactoryBean is have a property, algorithmName, on our bean and then use an initialization callback to call MessageDigest.getInstance(). Using a FactoryBean, we can encapsulate this logic inside a bean. Then any beans that require a MessageDigest instance can simply declare a property, messageDigest, and use the FactoryBean to obtain the instance. Listing 5-29 shows an implementation of FactoryBean that does just this.

Listing 5-29: The MessageDigestFactoryBean Class

image from book
package com.apress.prospring.ch5.factory;      import java.security.MessageDigest;      import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean;      public class MessageDigestFactoryBean implements FactoryBean, InitializingBean {          private String algorithmName = "MD5";          private MessageDigest messageDigest = null;          public Object getObject() throws Exception {        return messageDigest.clone();     }          public Class getObjectType() {        return MessageDigest.class;     }          public boolean isSingleton() {        return true;     }          public void afterPropertiesSet() throws Exception {         messageDigest = MessageDigest.getInstance(algorithmName);     }          public void setAlgorithmName(String algorithmName) {         this.algorithmName = algorithmName;     } }
image from book

The FactoryBean interface declares three methods: getObject(), getObjectType(), and isSingleton(). Spring calls the getObject() method to retrieve the Object created by the FactoryBean. This is the actual Object that is passed to other beans that use the FactoryBean as a collaborator. In Listing 5-29, you can see that the MessageDigestFactoryBean passes a clone of the stored MessageDigest instance that is created in the InitializingBean.afterPropertiesSet() callback.

The getObjectType() method allows you to tell Spring what type of Object your FactoryBean will return. This can be null if you know in advance, but if you specify a type, Spring can use it for auto-wiring purposes. We return MessageDigest as our type, because we do not know what concrete type will be returned, not that it matters because all beans will define their dependencies using MessageDigest anyway

The isSingleton() property allows you to inform Spring whether the FactoryBean is managing a singleton instance or not. Remember that by setting the singleton attribute of the FactoryBean's <bean> tag, you tell Spring about the singleton status of the FactoryBean itself, not the Objects it is returning.

Now let's see how the FactoryBean is employed in an application. In Listing 5-30, you can see a simple bean that maintains two MessageDigest instances and then displays the digests of a message passed to its digest() method.

Listing 5-30: The MessageDigester Class

image from book
package com.apress.prospring.ch5.factory;      import java.security.MessageDigest;      import sun.misc.BASE64Encoder;      public class MessageDigester {          private MessageDigest digest1 = null;     private MessageDigest digest2 = null;          public void setDigest1(MessageDigest digest1) {         this.digest1 = digest1;     }          public void setDigest2(MessageDigest digest2) {         this.digest2 = digest2;     }          public void digest(String msg) {         System.out.println("Using digest1");         digest(msg, digest1);              System.out.println("Using digest2");         digest(msg, digest2);     }          private void digest(String msg, MessageDigest digest) {         System.out.println("Using algorithm: " + digest.getAlgorithm());         digest.reset();         byte[] bytes = msg.getBytes();         byte[] out = digest.digest(bytes);         BASE64Encoder enc = new BASE64Encoder();         System.out.println(enc.encode(out));     } } 
image from book

Our apologies for using the Sun-specific BASE64Encoder class. We wanted to be able to display the digest data, but we didn't want to overcomplicate the example with the necessary code for Base64. If you're not running a Sun JVM, then you can remove the BASE64Encoder and output the bytes directly to the screen, or you can replace it with a class that performs a similar operation.

Listing 5-31 shows a simple BeanFactory configuration that configures two MessageDigestFactoryBeans, one for the SHA1 algorithm and the other using the default (MD5) algorithm.

Listing 5-31: Configuring FactoryBeans

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             >         <property name="algorithmName">             <value>SHA1</value>         </property>     </bean>     <bean             />     <bean             >         <property name="digest1">             <ref local="shaDigest"/>         </property>         <property name="digest2">             <ref local="defaultDigest"/>         </property>     </bean> </beans>
image from book

As you can see, not only have we configured the two MessageDigestFactoryBeans, but we have also configured a MessageDigester, using the two MessageDigestFactoryBeans, to provide values for the digest1 and digest2 properties. In Listing 5-32, you see a basic example class that retrieves the MessageDigester bean from the BeanFactory and creates the digest of a simple message.

Listing 5-32: Using MessageDigester

image from book
package com.apress.prospring.ch5.factory;      import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class MessageDigestExample {          public static void main(String[] args) {         BeanFactory factory = new XmlBeanFactory(new FileSystemResource(                 "./ch5/src/conf/factory/factory.xml"));              MessageDigester digester = (MessageDigester) factory                 .getBean("digester");         digester.digest("Hello World!");          } }
image from book

Running this example gives the following output:

Using digest1 Using algorithm: SHA1 Lve95gjOVATpfV8EL5X4nxwjKHE= Using digest2 Using algorithm: MD5 7Qdih1MuhjZehB6Sv8UNjA==

As you can see, the MessageDigest bean is provided with two MessageDigest implementations, SHA1 and MD5, despite the fact that no MessageDigests are configured in the BeanFactory. This is the FactoryBean at work.

FactoryBeans are the perfect solution when you are working with classes that cannot be created using the new operator. If you work with objects that are created using a factory method and you want to use these classes in a Spring application, then create a FactoryBean to act as an adaptor, allowing your classes to take full advantage of Spring's IoC capabilities.

Accessing a FactoryBean Directly

Given that Spring automatically satisfies any references to a FactoryBean by the objects produced by that FactoryBean, you may be wondering if you can actually access the FactoryBean directly. The answer is "Yes."

Accessing the FactoryBean is actually very simple: you simply prefix the bean name with an ampersand in the call to getBean(), as shown in Listing 5-33.

Listing 5-33: Accessing FactoryBeans Directly

image from book
package com.apress.prospring.ch5.factory;      import java.security.MessageDigest;      import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class AccessingFactoryBeans {          public static void main(String[] args) {         BeanFactory factory = new XmlBeanFactory(new FileSystemResource(                 "./ch5/src/conf/factory/factory.xml"));              MessageDigest digest =                                      (MessageDigest) factory.getBean("shaDigest");                  MessageDigestFactoryBean factoryBean =                          (MessageDigestFactoryBean) factory.getBean("&shaDigest");     } }
image from book

This feature is used in a few places in the Spring code, but your application should really have no reason to use it. The FactoryBean interface is intended to be used as a piece of supporting infrastructure to allow you to use more of your application's classes in an IoC setting. Avoid accessing the FactoryBean directly and invoking getObject() manually; if you do not, you are making extra work for yourself and are unnecessarily coupling your application to a specific implementation detail that could quite easily change in the future.



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