The Spring Framework provides tons of functionality for someone wanting to use dependency injection; so much so that it can be difficult to know everything you can do.
Many times when dealing with a framework or a web application, it becomes important to track down JAR files and class files at runtime, and load them, often times in their own ClassLoader object.
Just to remind everyone out there, classes loaded in one ClassLoader are not visible to a parent or sibling ClassLoader (unless you are using some nightmare ClassLoader object that breaks the hierarchical semantics of core Java classloading).
ClassLoaderA | (ClassA, ClassB, ClassC) | |__ ClassLoaderB (Child of A) | (ClassD, ClassE) | |__ ClassLoaderC (Child of A) (ClassF, ClassG)
Using this diagram as an example,
ClassLoaderB can see
ClassE, but it cannot see
ClassLoaderC cannot see the classes loaded by
ClassLoaderB. And finally,
ClassLoaderA can only see it's own classes.
Thankfully, objects loaded by child/sibling classloaders can be dealt with in classes loaded by parent/peer classloaders (this can be seen when you put one of your objects in to an
ArrayList object - the
ArrayList may not be able to reference your class, but it can still deal with the objects inside of that class.
Considering the above, what if we want to use a Spring
XMLBeanFactory object (created by
ClassLoaderA) to load a bean definition in an XML file that is referring to
ClassD which is inside
Thankfully, it is a fairly simple process; it simply involves understanding the two pieces at work. We have:
- A Bean Definition Reader - This is the object that reads the bean definition from the source (redundant, aren't I?); in other words, in this case this is the XML parser and magical finder of Class objects when you type
- A Bean Factory - This is the implementation of the factory that handles the construction of beans after they have been defined by the reader. This is the stage in the process where AOP, autowiring, property setting, singletons, prototypes, etc is all plugged in.
ClassLoaderBfor the task at hand.
BeanDefinitionReader is where classes are found when Spring is parsing/interpreting a bean definition source (such as a bean XML file). There is a method on the interface -
BeanDefinitionReader.getBeanClassLoader() that provides the correct class loader to the BeanFactory (this defaults to
Thread.currentThread().getContextClassLoader()). There is also a method on the abstract base implementation -
AbstractBeanDefinitionReader.setBeanClassLoader(ClassLoader) so we can set our class loader to the correct implementation.
'But wait R.J.!', you say - 'There is no
BeanDefinitionReader that I can see on the
XmlBeanFactory class - you're a liar! I'm never reading Coffee-Bytes again!'. Don't pull away from me yet! While it is true that the
XmlBeanFactory class doesn't provide visibility to the bean definition reader, that doesn't mean it's not there! In reality, the
XmlBeanFactory is just a convenience implementation of two seperate objects:
XmlBeanFactory factory = new XmlBeanFactory([xml resource here!]); // == to XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(reader); reader.loadBeanDefinitions([xml resource here!]);
Do I think it is frustrating that you can't get to the bean definition reader without forgoing the XmlBeanFactory class for it's super class? Absolutely! Do I have commit rights to Spring? Unfortunately for them, no (hint, hint). So, that's that... oh, you still don't feel that sweet cut/paste solution beckoning to you? Am I leaving you patient readers hanging without a clean solution to your problems? Ok, ok...
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(); reader.setBeanClassLoader([Class LoaderB here!]); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(reader); reader.loadBeanDefinitions([xml resource here!]);