The Spring ApplicationContext

So far, all interaction with Spring has been via the BeanFactory interface and its subinterfaces. Although using the BeanFactory interface is a good way of interacting with Spring for simple applications, it can prove unwieldy when used in larger applications. Recall that in some

of the previous examples we had to call control methods on the BeanFactory, such as preInstantiateSingletons(), or we had to invoke a BeanFactoryPostProcessor manually, as in the case of the CustomEditorConfigurer. This is where the ApplicationContext comes in.

ApplicationContext is an extension of BeanFactory, providing all the same functionality, but it also reduces the amount of code you need to interact with it and adds new features into the pot for good measure. When using an ApplicationContext, you can control bean instantiation declaratively on a bean-by-bean basis, and any BeanFactoryPostProcessors registered in the ApplicationContext are executed for you automatically.

The main function of the ApplicationContext is to provide a much richer framework on which to build your applications. An ApplicationContext is much more aware of the beans that you configure within it, and in the case of many of the Spring infrastructure classes and interfaces, such as BeanFactoryPostProcessor, it interacts with them on your behalf, reducing the amount of code you need to write in order to use Spring.

The biggest benefit of using ApplicationContext is that it allows you to configure and manage Spring and Spring-managed resources in a completely declarative way. This means that wherever possible, Spring provides support classes to load an ApplicationContext into your application automatically, thus removing the need for you to write any code to access the ApplicationContext. In practice, this feature is currently only available when you are building web applications with Spring, so Spring also provides implementations of ApplicationContext you can create yourself.

In addition to providing a model that is focused more on declarative configuration, the ApplicationContext supports the following features not present in a BeanFactory:

  • Internationalization

  • Event publication

  • Resource management and access

  • Additional lifecycle interfaces

  • Improved automatic configuration of infrastructure components

So should you use ApplicationContext or BeanFactory? Unless you are looking for a really lightweight IoC solution for your application, you should almost certainly use ApplicationContext. The additional support functionality provided by ApplicationContext really makes your life easier, reducing the amount of code you need to write and providing some useful additional features. When you are building a web application with Spring, having an ApplicationContext provided for you automatically makes choosing ApplicationContext over BeanFactory a real no-brainer.

Implementations of ApplicationContext

Like BeanFactory, ApplicationContext is an interface, and you are free to provide your own implementations. Of course, creating an ApplicationContext is no trivial feat, so Spring provides three implementations intended for use in production application. All three implementations use the same configuration format as the XmlBeanFactory. In fact, as you will see, they offer more complete support for the format than XmlBeanFactory.

For stand-alone applications where the ApplicationContext cannot be loaded automatically, you can choose from either FileSystemXmlApplicationContext or ClasspathXmlApplicationContext. These names are pretty self-explanatory, and functionally, these classes are quite similar. With FileSystemXmlApplicationContext, you can load the configuration from anywhere in the file system provided your application has permissions. Using ClasspathXmlApplicationContext, you can load from anywhere on the classpath; this is useful if you want to package the configuration with a bunch of classes inside a JAR file.

The XmlWebApplicationContext is intended solely for use in a web application environment, and as you will see in Chapter 17, by using either ContextLoaderListener or ContextLoaderServlet, you can load the ApplicationContext configuration automatically for your web application.

For the rest of the chapter, we will be using the FileSystemXmlApplicationContext to run the examples.

Using ApplicationContextAware

Earlier in the chapter you saw how a bean can obtain a reference to its BeanFactory by implementing the BeanFactoryAware interface. In the same way, a bean can obtain a reference to its ApplicationContext by implementing ApplicationContextAware. Listing 5-40 shows a bean that implements this interface.

Listing 5-40: Implementing ApplicationContextAware

image from book
package com.apress.prospring.ch5.context;      import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class ContextAwareDemo implements ApplicationContextAware {          private ApplicationContext ctx;          public void setApplicationContext(ApplicationContext applicationContext)             throws BeansException {         ctx = applicationContext;     }          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ch5/src/conf/appContext/aware.xml");                  ContextAwareDemo demo = (ContextAwareDemo) ctx.getBean("contextAware");         demo.displayAppContext();     }          public void displayAppContext() {         System.out.println(ctx);     } }
image from book

As you can see, the ApplicationContextAware interface declares a single method, setApplicationContext(), and implementing the interface is very much like implementing BeanFactoryAware. In the main() method, we create an instance of FileSystemXmlApplicationContext, and from this, we obtain an instance of the ContextAwareDemo bean. Listing 5-41 shows the configuration for this example.

Listing 5-41: Configuration for ContextAwareDemo Class

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

Notice that although we are using ApplicationContext and not BeanFactory, the configuration format is exactly the same, meaning that using ApplicationContext is no more difficult than using BeanFactory. Running this example gives the following output:

org.springframework.context.support.FileSystemXmlApplicationContext: ¿ displayName=[org.springframework.context.support.FileSystemXmlApplicationContext;¿ hashCode=26281671]; ¿ startup date=[Fri Aug 06 16:02:33 BST 2004]; ¿ root of ApplicationContext hierarchy

As you can see, the ContextAwareDemo is able to obtain a reference to its ApplicationContext and display its details. The same comments about the use of the BeanFactoryAware interface also apply to this interface.

Controlling Bean Initialization

Recall that in an earlier example, we built a ShutdownHookBean class that automatically registered a shutdown hook Thread with the JVM to dispose of all singletons in the BeanFactory. You might also remember that in order to ensure that the ShutdownHookBean was instantiated, we had to call the preInstantiateSingletons() method of the BeanFactory. This is slightly annoying because it means that an application has to have prior knowledge of the configuration; it also means that all singletons, not just the one we want, are instantiated in advance.

When using ApplicationContext, there is a solution to this problem: the lazy-init attribute. By setting the lazy-init attribute on a bean's <bean> tag to false, you are telling the ApplicationContext that you want to create the bean in advance and it should not wait until it is first requested. Listing 5-42 shows a revised configuration for the shutdown hook bean example.

Listing 5-42: Using lazy-init

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>3     </bean>     <bean                         lazy-init="false"/> </beans>
image from book

Notice that for the shutdownHook bean, we set the lazy-init attribute to false. Although lazy-init is false by default in many of the Spring implementations of ApplicationContext, there is no harm in making it explicit—this way the ApplicationContext implementation won't affect your bean. In Listing 5-43, you can see a revised driver application for this example, which omits the call to preInstantiateSingletons().

Listing 5-43: The LazyInitDemo Class

image from book
package com.apress.prospring.ch5.context;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      import com.apress.prospring.ch5.lifecycle.DestructiveBeanWithInterface;      public class LazyInitDemo {          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ch5/src/conf/appContext/lazy.xml");              DestructiveBeanWithInterface bean =  (DestructiveBeanWithInterface) ctx.getBean("destructiveBean");     } }
image from book

Running this example results in the same output as before, but without the need to call preInstantiateSingletons():

Initializing Bean Destroying Singletons Destroying Bean Singletons Destroyed

This is clearly beneficial because it lets you have fine-grained control over when each bean in your application is created without having to modify any of the application code.

Internationalization with MessageSource

One area in which Spring really excels is in support for internationalization (i18n). Using the MessageSource interface, your application can access String resources, called messages, stored in a variety of different languages. For each language you want to support in your application, you maintain a list of messages that are keyed to correspond to messages in other languages. For instance, if I wanted to display "The quick brown fox jumped over the lazy dog" in English and in Czech, I would create two messages, both keyed as msg; the one for English would say, "The quick brown fox jumped over the lazy dog," and the one for Czech would say, "Príšerne žlutoucký kun úpel dábelské ódy."

Although you don't need to use ApplicationContext to use MessageSource, the ApplicationContext interface actually extends MessageSource and provides special support for loading messages and for making them available in your environment. The automatic loading of messages is available in any environment, but automatic access is only provided in certain Spring-managed scenarios, such as when you are using Spring's MVC framework to build a web application. Although any class can implement ApplicationContextAware and thus access the automatically loaded messages, we suggest a better solution later in this chapter in the section entitled "Using MessageSource in Stand-Alone Applications."

Before we continue, if you are unfamiliar with i18n support in Java, we suggest that you at least check out the JavaDocs for the Locale and ResourceBundle classes.

Using ApplicationContext and MessageSource

Aside from ApplicationContext, Spring provides three MessageSource implementations: ResourceBundleMessageSource, ReloadableResourceMessageSource, and StaticMessageSource. The StaticMessageSource is not really meant to be used in a production application because you can't configure it externally, and this is generally one of the main requirements when you are adding i18n capabilities to your application. The ResourceBundleMessageSource loads messages using a Java ResourceBundle. ReloadableResourceMessageSource is essentially the same, except it supports scheduled reloading of the underlying source files.

All of the implementations, ApplicationContext included, implement another interface HierarchicalMessageSource, which allows for many MessageSource instances to be nested. This is key to the way ApplicationContext works with MessageSources.

To take advantage of ApplicationContext's support for MessageSource, you must define a bean in your configuration of type MessageSource and with the name messageSource. ApplicationContext takes this MessageSource and nests it within itself, allowing you to access the messages using the ApplicationContext. This can be hard to visualize, so take a look at the following example.

Listing 5-44 shows a simple application that accesses a set of messages for both the English and Czech locales.

Listing 5-44: Exploring MessageSource Usage

image from book
package com.apress.prospring.ch5.context;      import java.util.Locale;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class MessageSourceDemo {          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ch5/src/conf/appContext/messageSource.xml");              Locale english = Locale.ENGLISH;         Locale czech = new Locale("cs", "CZ");              System.out.println(ctx.getMessage("msg", null, english));         System.out.println(ctx.getMessage("msg", null, czech));                  System.out.println(ctx.getMessage("nameMsg", new Object[] { "Rob",                 "Harrop" }, english));     } } 
image from book

Don't worry about the calls to getMessage() just yet; we return to those shortly. For now, just know that they retrieve a keyed message for the locale specified. In Listing 5-45 you can see the configuration used by this application.

Listing 5-45: Configuring a MessageSource Bean

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean   >             <property name="basenames">                 <list>                     <value>buttons</value>                     <value>labels</value>                 </list>                 </property>     </bean> </beans>
image from book

Here we are defining a ResourceBundleMessageSource bean with the name messageSource as required and configuring it with a set of names to form the base of its file set. A Java ResourceBundle, which is used by ResourceBundle, works on a set of properties files that are identified by base names. When looking for a message for a particular Locale, the ResourceBundle looks for a file that is named as a combination of the base name and the Locale name. For instance, if the base name is foo and we are looking for a message in the en-GB (British English) Locale, then the ResourceBundle looks for a file called foo_en_GB.properties.

Running this application (with the appropriate ResourceBundle files created and present in the classpath) yields the following output:

The quick brown fox jumped over the lazy dog Príšerne žlutoucký kun úpel dábelské ódy My name is Rob Harrop

The translation of the Czech is "Terribly yellow horse was groaning devilish odes." Now this example just raises even more questions. What did those calls to getMessage() mean? Why did we use ApplicationContext.getMessage() rather than access the ResourceBundleMessageSource bean directly? We'll answer each of these questions in turn.

The getMessage() Method

The MessageSource interface defines three overloads for the getMessage() method. These are described in Table 5-2.

Table 5-2: Overloads for MessageSource.getMessage()

Method Signature

Description

getMessage(String, Object[], Locale)

This is the standard getMessage() method. The String argument is the key of the message corresponding to the key in the properties file. In Listing 5-44, the first call to getMessage() used msg as the key, and this corresponded to the following entry in the properties file for the en locale: msg=The quick brown fox jumped over the lazy dog. The Object[] array argument is used for replacements in the message. In the third call to getMessage() in Listing 5-44, we passed in an array of two Strings. The message that was keyed as nameMsg was My name is {0} {1}. The numbers surrounded in braces are placeholders, and each one is replaced with the corresponding entry in the argument array. The final argument, Locale, tells ResourceBundleMessageSource which properties file to look in. Even though the first and second calls to getMessage() in the example used the same key, they returned different messages that correspond to the Locale that was passed in to getMessage().

getMessage(String, Object[], String, Locale)

This overload works in the same way as getMessage(String, Object[], Locale), other than the second String argument, which allows you to pass in a default value in case a message for the supplied key is not available for the supplied Locale.

getMessage(MessageSourceResolvable, Locale)

This overload is a special case. We discuss it in further detail in the section entitled "The MessageSourceResolvable Interface."

Why Use ApplicationContext as a MessageSource?

To answer this question, we need to jump a little ahead of ourselves and look at the web application support in Spring. The answer, in general, to this question is that you shouldn't use the ApplicationContext as a MessageSource when doing so couples your bean to the Application- Context unnecessarily (this is discussed in more detail in the next section). You should use the ApplicationContext when you are building a web application using Spring's MVC framework.

The core interface in Spring MVC is Controller. Unlike frameworks like Struts that require that you implement your controllers by inheriting from a concrete class, Spring simply requires that you implement the Controller interface. Having said that, Spring provides a collection of useful base classes that you will, more often than not, use to implement your own controllers. All of these base classes are themselves subclasses (directly or indirectly) of the ApplicationObjectSupport class.

Remember that in a web application setting, the ApplicationContext is loaded automatically. ApplicationObjectSupport accesses this ApplicationContext, wraps it in a MessageSourceAccessor object, and makes that available to your controller via the protected getMessageSourceAccessor() method. MessageSourceAccessor provides a wide array of convenience methods for working with MessageSources. This form of "auto injection" is quite beneficial; it removes the need for all of your controllers to expose a messageSource property.

However, this is not the best reason for using ApplicationContext as a MessageSource in your web application. The main reason to use ApplicationContext rather than a manually defined MessageSource bean is that Spring does, where possible, expose ApplicationContext, as a MessageSource, to the view tier. This means that when you are using Spring's JSP tag library, the <spring:message> tag automatically reads messages from the ApplicationContext, and when you are using JSTL, the <fmt:message> tag does the same.

All of these benefits mean that it is better to use the MessageSource support in ApplicationContext when you are building a web application, rather than manage an instance of MessageSource separately. This is especially true when you consider that all you need to do to take advantage of this feature is configure a MessageSource bean with the name messageSource.

Using MessageSource in Stand-Alone Applications

When you are using MessageSources in stand-alone applications where Spring offers no additional support other than to nest the messageSource bean automatically in the ApplicationContext, it is best to make the MessageSources available using Dependency Injection. You can opt to make your bean ApplicationContextAware, but doing so precludes their use in a BeanFactory context. Add to this the fact that you complicate testing without any discernible benefit, and it is clear that you should stick to using Dependency Injection to access MessageSource objects in a stand-alone setting.

The MessageSourceResolvable Interface

You can use an Object that implements MessageSourceResolvable in place of a key and a set of arguments when you are looking up a message from a MessageSource. This interface is most widely used in the Spring validation libraries to link Error objects to their internationalized error messages. You will see an example of how to use MessageSourceResolvable in Chapter 17 when we look at error handling in the Spring MVC library.

Using Application Events

Another feature of the ApplicationContext not present in the BeanFactory is the ability to publish and receive events using the ApplicationContext as a broker. An event is class-derived from ApplicationEvent, which itself derives from the java.util.EventObject. Any bean can listen for events by implementing the ApplicationListener interface; the ApplicationContext automatically registers any bean that implements this interface as a listener when it is configured. Events are published using the ApplicationContext.publishEvent() method, so the publishing class must have knowledge of the ApplicationContext. In a web application, this is simple because many of your classes are derived from Spring framework classes that allow access to the ApplicationContext through a protected method. In a stand-alone application, you can have your publishing bean implement ApplicationContextAware to enable it to publish events.

Listing 5-46 shows an example of a basic event class.

Listing 5-46: Creating an Event Class

image from book
package com.apress.prospring.ch5.event;      import org.springframework.context.ApplicationEvent;      public class MessageEvent extends ApplicationEvent {          private String msg;     public MessageEvent(Object source, String msg) {         super(source);         this.msg = msg;     }          public String getMessage() {         return msg;     }  }
image from book

This code is quite basic; the only point of note is that the ApplicationEvent has a single constructor that accepts a reference to the source of the event. This is reflected in the constructor for MessageEvent. In Listing 5-47 you can see the code for the listener.

Listing 5-47: The MessageEventListener Class

image from book
package com.apress.prospring.ch5.event;      import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener;      public class MessageEventListener implements ApplicationListener {              public void onApplicationEvent(ApplicationEvent event) {        if(event instanceof MessageEvent) {            MessageEvent msgEvt = (MessageEvent)event;            System.out.println("Received: " + msgEvt.getMessage());        }     } }
image from book

The ApplicationListener interface defines a single method, onApplicationEvent, that is called by Spring when an event is raised. The MessageEventListener is only interested in events of type MessageEvent, so it checks to see whether the event raised is of that type and, if so, it writes the message to stdout. Publishing events is simple; it is just a matter of creating an instance of the event class and passing it to the ApplicationContext.publishEvent() method, as shown in Listing 5-48.

Listing 5-48: Publishing an Event

image from book
package com.apress.prospring.ch5.event;      import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class Publisher implements ApplicationContextAware {          private ApplicationContext ctx;          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ch5/src/conf/events/events.xml");              Publisher pub = (Publisher) ctx.getBean("publisher");         pub.publish("Hello World!");         pub.publish("The quick brown fox jumped over the lazy dog");     }          public void setApplicationContext(ApplicationContext applicationContext)             throws BeansException {         this.ctx = applicationContext;          }          public void publish(String message) {         ctx.publishEvent(new MessageEvent(this, message));     } }
image from book

Here you can see that the Publisher class retrieves an instance of itself from the ApplicationContext and then, using the publish() method, publishes two MessageEvents to the ApplicationContext. The Publisher bean instance accesses the ApplicationContext by implementing ApplicationContextAware. Listing 5-49 shows the configuration for this example.

Listing 5-49: Configuring ApplicationListener Beans

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

Notice that you do not need a special configuration to register the MessageEventListener with the ApplicationContext; it is picked up automatically by Spring. Running this example results in the following output:

Received: Hello World! Received: The quick brown fox jumped over the lazy dog

Considerations for Event Usage

There are many cases in an application where certain components need to be notified of certain events. Often you do this by writing code to notify each component explicitly or by using a messaging technology such as JMS. The drawback of writing code to notify each component in turn is that you are coupling those components to the publisher, in many cases unnecessarily.

Consider a situation where you cache product details in your application to avoid trips to the database. Another component allows product details to be modified and persisted to the database. To avoid making the cache invalid, the update component explicitly notifies the cache that the user details have changed. In this example, the update component is coupled to a component that, really, has nothing to do with its business responsibility. A better solution would be to have the update component publish an event every time a product's details are modified, and then have interested components, such as the cache, listen for that event. This has the benefit of keeping the components decoupled, which makes it simple to remove the cache if you need to, or to add another listener that is interested in knowing when a product's details change.

Using JMS in this case would be overkill, because the process of invalidating the product's entry in the cache is quick and is not business critical. The use of the Spring event infrastructure adds very little overhead to your application.

Typically, we use events for reactionary logic that executes quickly and is not part of the main application logic. In the previous example, the invalidation of a product in cache happens in reaction to the updating of product details, it executes quickly (or it should), and it is not part of the main function of the application. For processes that are long running and form part of the main business logic, we prefer to use JMS or similar messaging systems such as MSMQ in the Microsoft world. In a recent project we built an e-commerce system with some complex order fulfillment logic. For this, we chose to use JMS because it is more suited to long-running processes and as the system grows, we can, if necessary, factor the JMS-driven order processing onto a separate machine.

Accessing Resources

Often an application needs to access a variety of resources in different forms. You might need to access some configuration data stored in a file in the file system, some image data stored in a JAR file on the classpath, or maybe some data on a server elsewhere. Spring provides a unified mechanism for accessing resources in a protocol-independent way. This means that your application can access a file resource in the same way, whether it is stored in the file system, the classpath, or on a remote server.

At the core of Spring's resource support is the Resource interface. The Resource interface defines seven self-explanatory methods: exists(), getDescription(), getFile(), getFileName(), getInputStream(), getURL(), and isOpen(). In addition to these seven methods, there is one that is not quite so self-explanatory: createRelative(). The createRelative() method creates a new Resource instance using a path that is relative to the instance on which it is invoked. You can provide your own Resource implementations, although that is outside the scope of this chapter, but in most cases, you use one of the built-in implementations for accessing file, classpath, or URL resources.

Internally, Spring uses another interface, ResourceLoader, and the default implementation, DefaultResourceLoader, to locate and create Resource instances. However, you generally won't interact with DefaultResourceLoader, instead using another ResourceLoader implementation— ApplicationContext.

Listing 5-50 shows a sample application that accesses three resources using ApplicationContext.

Listing 5-50: Accessing Resources

image from book
package com.apress.prospring.ch5.resource;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.core.io.Resource;      public class ResourceDemo {          public static void main(String[] args) throws Exception{         ApplicationContext ctx = new FileSystemXmlApplicationContext(         "./ch5/src/conf/events/events.xml");            Resource res1 = ctx.getResource("file:///d:/tmp/test.txt");         displayInfo(res1);         Resource res2 = ctx.getResource("classpath:lib/commons-logging.jar");         displayInfo(res2);         Resource res3 = ctx.getResource("http://www.google.co.uk");         displayInfo(res3);     }          private static void displayInfo(Resource res) throws Exception{         System.out.println(res.getClass());         System.out.println(res.getURL().getContent());         System.out.println("");     } }
image from book

You should note that the configuration file used in this example is unimportant. Notice that in each call to getResource() we pass in a URI for each resource. You will recognize the common file: and http: protocols that we pass in for res1 and res3. The classpath: protocol we use for res2 is Spring-specific and indicates that the ResourceLoader should look in the classpath for the resource. Running this example results in the following output:

class org.springframework.core.io.UrlResource sun.net.www.content.text.PlainTextInputStream@4cee32     class org.springframework.core.io.ClassPathResource java.io.BufferedInputStream@12b7eea     class org.springframework.core.io.UrlResource sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@cd5f8b

Notice that for both the file: and http: protocols, Spring returns a UrlResource instance. Spring does include a FileSystemResource class, but the DefaultResourceLoader does not use this class at all. Once a Resource instance is obtained, you are free to access the contents as you see fit, using getFile(), getInputStream(), or getURL(). In some cases, such as when you are using the http: protocol, the call to getFile() results in a FileNotFoundException. For this reason, we recommend that you use getInputStream() to access resource contents because it is likely to function for all possible resource types.



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