Making Your Beans

Making Your Beans "Spring Aware"

One of the biggest selling points of Dependency Injection over Dependency Lookup as a mechanism for achieving Inversion of Control is that your beans do not need to be aware of the implementation of the container that is managing them. To a bean that uses constructor injection, the Spring container is the same as the container provided by PicoContainer. However, in certain circumstances, you may need a bean that is using Dependency Injection to obtain its dependencies so it can interact with the container for some other reason. An example of this may be a bean that automatically configures a shutdown hook for you, and thus it needs access to the BeanFactory. In other cases, a bean may wish to know what its name is so it can perform some additional processing based on this name.

That said, this feature is really intended for internal Spring use. Giving the name some kind of business meaning is generally a bad idea and can lead to configuration problems where bean names have to be artificially manipulated to support their business meaning. However, we have found that being able to have a bean find out its name at runtime is really useful for logging. Consider a situation where you have many beans of the same type running under different configurations. The bean name can be included in log messages to help you differentiate between which one is generating errors and which ones are working fine when something goes wrong.

Using the BeanNameAware Interface

The BeanNameAware interface, which can be implemented by a bean that wants to obtain its own name, has a single method: setBeanName(String). Spring calls the setBeanName() method after it has finished configuring your bean but before any lifecycle callbacks (initialization or destroy) are called. In most cases, the implementation of the setBeanName() interface is just a single line that stores the value passed in by the container in a field for use later on. Listing 5-11 shows a simple bean that obtains its name using BeanNameAware and then later uses this bean name when writing log messages.

Listing 5-11: Implementing BeanNameAware

image from book
package com.apress.prospring.ch5.interaction;      import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanNameAware;      public class LoggingBean implements BeanNameAware {          private static final Log log = LogFactory.getLog(LoggingBean.class);          private String beanName = null;          public void setBeanName(String beanName) {         this.beanName = beanName;     }          public void someOperation() {         if(log.isInfoEnabled()) {             log.info("Bean [" + beanName + "] - someOperation()");         }     } }
image from book

This implementation is fairly trivial. Remember that BeanNameAware.setBeanName() is called before the first instance of the bean is returned to your application via a call to BeanFactory.getBean(), so there is no need to check to see if the bean name is available in the someOperation() method. Listing 5-12 shows a simple configuration for this example.

Listing 5-12: Configuring the LoggingBean Example

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             /> </beans>
image from book

As you can see, no special configuration is required to take advantage of the BeanNameAware interface. In Listing 5-13 you can see a simple example application that retrieves the Logging- Bean instance from the BeanFactory and then calls the someOperation() method.

Listing 5-13: The LoggingBeanExample Class

image from book
package com.apress.prospring.ch5.interaction;      import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class LoggingBeanExample {          public static void main(String[] args) {         BeanFactory factory = new XmlBeanFactory(new FileSystemResource(                 "./ch5/src/conf/interaction/logging.xml"));                  LoggingBean bean = (LoggingBean)factory.getBean("loggingBean");         bean.someOperation();     } }
image from book

This example generates the following log output—notice the inclusion of the bean name in the log message for the call to someOperation():

Aug 4, 2004 4:10:35 PM ¿ org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from file ¿ [D:\projects\pro-spring\.\ch5\src\conf\interaction\logging.xml] Aug 4, 2004 4:10:35 PM ¿ org.springframework.beans.factory.support.AbstractBeanFactory getBean INFO: Creating shared instance of singleton bean 'loggingBean' Aug 4, 2004 4:10:35 PM ¿ com.apress.prospring.ch5.interaction.LoggingBean someOperation INFO: Bean [loggingBean] - someOperation()

Using the BeanNameAware interface is really quite simple, and it is put to good use when you are improving the quality of your log messages. Avoid being tempted to give your bean names business meaning just because you can access them; by doing so, you are coupling your classes to Spring for a feature that brings negligible benefit. If your beans need some kind of name internally, then have them implement an interface such as Nameable with a method setName() and then give each bean a name using Dependency Injection. This way you can keep the names you use for configuration concise and you won't need to manipulate your configuration unnecessarily to give your beans names with business meaning.

Using the BeanFactoryAware Interface

Using the BeanFactoryAware method, it is possible for your beans to get a reference to the BeanFactory that configured them. The main reason this interface was created was to allow a bean to access other beans programmatically, using getBean(). You should, however, avoid this practice and use Dependency Injection to provide your beans with their collaborators. If you use the lookup-based getBean() approach to obtain dependencies when you can use Dependency Injection, you are adding unnecessary complexity to your beans and coupling them to the Spring framework without good reason.

Of course, the BeanFactory isn't just used to look up beans; it performs a great many other tasks. As you saw previously, one of these tasks is to destroy all singletons, notifying each of them in turn before doing so. In the previous section, you saw how to create a shutdown hook to ensure that the BeanFactory is instructed to destroy all singletons before the application shuts down. By using the BeanFactoryAware interface, you can build a bean that can be configured in a BeanFactory to create and configure a shutdown hook bean automatically. Listing 5-14 shows the code for this bean.

Listing 5-14: The ShutdownHookBean Class

image from book
package com.apress.prospring.ch5.interaction;      import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;      public class ShutdownHookBean implements BeanFactoryAware,         Runnable {          private ConfigurableListableBeanFactory factory;          public void setBeanFactory(BeanFactory factory) throws BeansException {              if (factory instanceof ConfigurableListableBeanFactory) {             this.factory = (ConfigurableListableBeanFactory) factory;             Runtime.getRuntime().addShutdownHook(new Thread(this));         }     }          public void run() {         if (factory != null) {             System.out.println("Destroying Singletons");             factory.destroySingletons();             System.out.println("Singletons Destroyed");         }     } }
image from book

Most of this code should seem familiar to you by now. The BeanFactoryAware interface defines a single method, setBeanFactory(BeanFactory), which Spring calls to pass your bean a reference to its BeanFactory. In Listing 5-14, the ShutdownHookBean class checks to see if the BeanFactory is of type ConfigurableListableBeanFactory, meaning it has a destroySingletons() method; if it does, it saves the reference to a field. You can also see that if the BeanFactory is a ConfigurableListableBeanFactory, the bean registers itself as a shutdown hook with the current Runtime instance. Listing 5-15 shows how to configure this bean to work with the DestructiveBeanWithInterface bean used in the previous section.

Listing 5-15: Configuring the ShutdownHookBean

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             >          <property name="filePath">             <value>d:/tmp/test.txt</value>         </property>     </bean>     <bean               /> </beans>
image from book

Notice that no special configuration is required. Listing 5-16 shows a simple example application that uses the ShutdownHookBean to manage the destruction of singleton beans.

Listing 5-16: Using ShutdownHookBean

image from book
package com.apress.prospring.ch5.interaction;      import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      import com.apress.prospring.ch5.lifecycle.DestructiveBeanWithInterface; public class ShutdownHookBeanExample {          public static void main(String[] args) {         ConfigurableListableBeanFactory factory = new XmlBeanFactory(                 new FileSystemResource(                         "./ch5/src/conf/interaction/shutdownHook.xml"));              // make sure the shutdown hook is created         factory.preInstantiateSingletons();              DestructiveBeanWithInterface bean =           (DestructiveBeanWithInterface)factory.getBean("destructiveBean");     } } 
image from book

This code should seem quite familiar to you, but note that we have included a call to ConfigurableListableBeanFactory.preInstantiateSingletons(). By default, Spring lazily instantiates singleton beans as they are needed. This is a problem for the ShutdownHookBean, because it needs to be instantiated to register itself as a shutdown hook. Invoking preInstantiateSingletons() causes Spring to run through all its singleton bean definitions and create the instances, invoking any callback methods as appropriate. Running this example yields the following output, as expected:

Initializing Bean Destroying Singletons Destroying Bean Singletons Destroyed

As you can see, even though no calls to destroySingletons() are in the main application, the ShutdownHookBean is registered as a shutdown hook and called destroySingletons() just before the application shut down.



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