Piotr Gabryanczyk’s Blog

Java, Refactoring, AOP, Spring, DDD, TDD, etc.

  • Blogroll

    • I have joined Anti-IF Campaign

Preventing Spring Context to be loaded more than once.

Posted by Piotr Gabryanczyk on March 21, 2007

Problem:

I have nested import statements in spring context files:

a.xml

<beans>
<import resource="b.xml"/>
<import resource="c.xml"/>
...
</beans>

b.xml

<beans>
<import resource="d.xml"/>
<import resource="e.xml"/>
...
</beans>

c.xml

<beans>
<import resource="d.xml"/>
<import resource="g.xml"/>
...
</beans>

When we load a.xml, both b.xml and c.xml will try to load d.xml. Spring will detect it on the bean level and will override beans already loaded and print “Overiding bean definition for bean” message. But it also means instantiation of overriden bean and lot of expensive preprocessing. (more about the problem…) In some cases (JMX bean registration, etc.) it might even cause spring context loader to stop loading the beans and closing the application.

Solution:

We want to prevent Spring from loading files already loaded. The following class will do this for us:

 1 import java.io.IOException;

 2 import java.net.URL;

 3 import java.util.Set;

 4 import java.util.TreeSet;

 5

 6 import org.springframework.beans.factory.BeanDefinitionStoreException;

 7 import org.springframework.beans.factory.support.DefaultListableBeanFactory;

 8 import org.springframework.beans.factory.xml.ResourceEntityResolver;

 9 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

10 import org.springframework.context.support.ClassPathXmlApplicationContext;

11 import org.springframework.core.io.Resource;

12

13 public class IgnoringDuplicateBeansClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

14     private Set<String> loadedLocations;

15

16     // (PG) Lazy init is needed as the method is called from superclass constructor

17     // (PG) I dont like it!!!

18     public Set<String> getLoadedLocations() {

19         if (loadedLocations == null) loadedLocations = new TreeSet<String>();

20         return loadedLocations;

21     }

22

23

24     public IgnoringDuplicateBeansClassPathXmlApplicationContext(String configFile) throws org.springframework.beans.BeansException {

25         super(new String[]{configFile});

26     }

27

28     public IgnoringDuplicateBeansClassPathXmlApplicationContext(String[] configFiles) {

29         super(configFiles);

30     }

31

32     protected DefaultListableBeanFactory createBeanFactory() {

33         return new IgnoringDuplicateBeansBeanFactory(getInternalParentBeanFactory());

34     }

35

36

37     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {

38         // Create a new XmlBeanDefinitionReader for the given BeanFactory.

39         XmlBeanDefinitionReader beanDefinitionReader = new IgnoringDuplicateFilesBeanDefinitionReader(beanFactory);

40

41         // Configure the bean definition reader with this context's

42         // resource loading environment.

43         beanDefinitionReader.setResourceLoader(this);

44         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

45

46         // Allow a subclass to provide custom initialization of the reader,

47         // then proceed with actually loading the bean definitions.

48         initBeanDefinitionReader(beanDefinitionReader);

49         loadBeanDefinitions(beanDefinitionReader);

50     }

51

52     public class IgnoringDuplicateFilesBeanDefinitionReader extends XmlBeanDefinitionReader {

53

54         public IgnoringDuplicateFilesBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {

55             super(beanFactory);

56         }

57

58         public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {

59             String location = getURL(resource).toString();

60             if (getLoadedLocations().contains(location)) {

61                 logger.warn("Ignoring already loaded context [" + resource.getFilename() + "] - " + location);

62                 return 0;

63             } else {

64                 getLoadedLocations().add(location);

65                 return super.loadBeanDefinitions(resource);

66             }

67         }

68

69         private URL getURL(Resource resource) {

70             try {

71                 return resource.getURL();

72             } catch (IOException e) {

73                 throw new RuntimeException(e);

74             }

75         }

76     }

77 }

78 
 1 import org.springframework.beans.factory.BeanDefinitionStoreException;

 2 import org.springframework.beans.factory.BeanFactory;

 3 import org.springframework.beans.factory.config.BeanDefinition;

 4 import org.springframework.beans.factory.support.DefaultListableBeanFactory;

 5 import org.springframework.util.Assert;

 6

 7 public class IgnoringDuplicateBeansBeanFactory extends DefaultListableBeanFactory {

 8     public IgnoringDuplicateBeansBeanFactory(BeanFactory beanFactory) {

 9         super(beanFactory);

10     }

11

12     public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

13             throws BeanDefinitionStoreException {

14

15         Assert.hasText(beanName, "'beanName' must not be empty");

16         Assert.notNull(beanDefinition, "Bean definition must not be null");

17         if (containsBeanDefinition(beanName)) {

18             logger.warn("Ignoring bean definition [" + beanDefinition + "] for bean '" + beanName +

19                     "': there's already [" + getBeanDefinition(beanName) + "] bound");

20         } else {

21             super.registerBeanDefinition(beanName, beanDefinition);

22         }

23     }

24

25 }

26 

You can use it as normal ClassPathXmlApplicationContext:

 1         ConfigurableApplicationContext ctx;

 2

 3         ctx = new IgnoringDuplicateBeansClassPathXmlApplicationContext("a.xml");

 4         SomeBean b = (SomeBean) ctx.getBean("xyzBean");
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: