Search This Blog

Sunday 2 December 2012

The ResourceLoaderAware Interface

In an earlier post we saw how using Spring's ApplicationContext we were able to access and load resources. However having that the ApplicationContext auto-wired in your class so as to allow you to use the Resource Loading functionality may seem a bit of an overkill. We after only need the ability to load resources. So ApplicationContext is out.
Instead we have the ResourceLoader interface.
This is the same interface that our ApplicationContext use internally to load resources. If we look in to the ApplicationContext hierarchy we see
public abstract class AbstractBeanDefinitionReader implements 
                        EnvironmentCapable, BeanDefinitionReader {
    /** Logger available to subclasses */
    protected final Log logger = LogFactory.getLog(getClass());
    private final BeanDefinitionRegistry registry;
    private ResourceLoader resourceLoader;
        // other code... Lots of it
}
The ApplicationContext delegates it resource loading activity to the ResourceLoader instance. So I changed my method to use the org.springframework.core.io.ResourceLoader directly:
final Resource template = this.resourceLoader.getResource("classpath:" + pathUrl);
Now the question is how to get access to the ResourceLoader?
For this Spring provides us with the ResourceLoaderAware interface. Just like the ApplicationContextAware interface, when the bean is being configured, the SpringContainer will wire our class with a reference to the ResourceLoader:
public class FileLoader implements ResourceLoaderAware {

    private ResourceLoader resourceLoader;
    
    @Override
    public void setResourceLoader(final ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

}
I added a simple method to test this interface:
public void testUrl() throws IOException {
    Resource urlResource = this.resourceLoader.getResource("http://www.google.com");
    System.out.println(urlResource.getURL() +", last modified is : "+urlResource.lastModified());
}
I decided to run the code using a BeanFactory as my SpringContainer:
public static void main(String[] args) throws IOException {
    Resource res = new ClassPathResource("beans.xml");
    XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(res);
    LoaderBean loaderBean = (LoaderBean) xmlBeanFactory.getBean("loaderBean");
    loaderBean.testUrl();
}
The result of the above code is:
Exception in thread "main" java.lang.NullPointerException
    at com.testResourceLoaderAware.LoaderBean.testUrl(LoaderBean.java:25)
    at com.testResourceLoaderAware.LoaderBean.main(LoaderBean.java:35)
However if I replace the BeanFactory with an ApplicationContext instance:
ApplicationContext xmlBeanFactory = new ClassPathXmlApplicationContext("beans.xml");
The output now is:
http://www.google.com, last modified is : 0
As can be seen the above interface does not work with Bean Factory. If we look at the docs for BeanFactory:
Bean factory implementations should support the standard bean lifecycle interfaces 
as far as possible. The full set of initialization methods and their standard order is:
1. BeanNameAware's setBeanName
2. BeanClassLoaderAware's setBeanClassLoader
3. BeanFactoryAware's setBeanFactory
4. ResourceLoaderAware's setResourceLoader (only applicable when running 
in an application context)
5. ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable 
when running in an application context)
6. MessageSourceAware's setMessageSource (only applicable when running in 
an application context)
7. ApplicationContextAware's setApplicationContext (only applicable when 
running in an application context)
8. ServletContextAware's setServletContext (only applicable when 
unning in a web application context)
9. postProcessBeforeInitialization methods of BeanPostProcessors
10. InitializingBean's afterPropertiesSet
11. a custom init-method definition
12. postProcessAfterInitialization methods of BeanPostProcessors
The BeanFactory in fact does not even provide us with the getResource method. If you have to use it in a BeanFactory environment, then you can stick to using the DefaultResourceLoader we saw in the last post. That one does not even need to be run inside a Spring Container.
public static void main(String[] args) throws IOException {
    DefaultResourceLoader defaultResourceLoader = new DefaultResourceLoader();
    Resource urlResource = defaultResourceLoader
        .getResource("http://www.google.com");
    System.out.println(urlResource.getURL() + ", last modified is : "
        + urlResource.lastModified());
}

No comments:

Post a Comment