stone2083

编程方式实现SpringBean LazyInit

Tags:
Spring  LazyInit DocumentDefaultsDefinition ReaderEventListener AbstractXmlApplicationContext

背景:
工程单元测试希望和生产环境应用共用一份Spring配置文件.
生产环境应用为了客户体验使用非LazyInit模式,但是单元测试下为了当前测试提高响应时间,希望设置LazyInit.

分析源代码,得知,Spring在解析XML时,会将Bean默认配置,放入到DocumentDefaultsDefinition对象中,其中包含lazyInit.
DocumentDefaultsDefinition注释如下:
Simple JavaBean that holds the defaults specified at the <beans> level in a standard Spring XML bean definition document: default-lazy-init, default-autowire, etc

Spring是否提供了入口点,进行DocumentDefaultsDefinition的修改呢?
详见:ReaderEventListener,注释如下:
Interface that receives callbacks for component, alias and import registrations during a bean definition reading process
在BeanDefinition分析过程中,对component,alias,import registrations,defaults registrations提供一组callbacks.

接口代码如下:
 1 public interface ReaderEventListener extends EventListener {
 2 
 3     /**
 4      * Notification that the given defaults has been registered.
 5      * @param defaultsDefinition a descriptor for the defaults
 6      * @see org.springframework.beans.factory.xml.DocumentDefaultsDefinition
 7      */
 8     void defaultsRegistered(DefaultsDefinition defaultsDefinition);
 9 
10     /**
11      * Notification that the given component has been registered.
12      * @param componentDefinition a descriptor for the new component
13      * @see BeanComponentDefinition
14      */
15     void componentRegistered(ComponentDefinition componentDefinition);
16 
17     /**
18      * Notification that the given alias has been registered.
19      * @param aliasDefinition a descriptor for the new alias
20      */
21     void aliasRegistered(AliasDefinition aliasDefinition);
22 
23     /**
24      * Notification that the given import has been processed.
25      * @param importDefinition a descriptor for the import
26      */
27     void importProcessed(ImportDefinition importDefinition);
28     
29 }

接下去分析,ReaderEventListener是在哪个入口点,提供了回调.答案是XmlBeanDefinitionReader.
在AbstractXmlApplicationContext,创建了XmlBeanDefinitionReader对象,见:
 1 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
 2     
 3     /**
 4      * Loads the bean definitions via an XmlBeanDefinitionReader.
 5      * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 6      * @see #initBeanDefinitionReader
 7      * @see #loadBeanDefinitions
 8      */
 9     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
10         // Create a new XmlBeanDefinitionReader for the given BeanFactory.
11         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
12 
13         // Configure the bean definition reader with this context's
14         // resource loading environment.
15         beanDefinitionReader.setResourceLoader(this);
16         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
17 
18         // Allow a subclass to provide custom initialization of the reader,
19         // then proceed with actually loading the bean definitions.
20         initBeanDefinitionReader(beanDefinitionReader);
21         loadBeanDefinitions(beanDefinitionReader);
22     }
23 
24 }

我们只需要去复写这个方法,在创建XmlBeanDefinitionReader的时候,去注入EventListener即可.

扩展代码如下:
LazyInitListener.java (不管配置文件如何配置,设置默认的LazyInit为true)
public class LazyInitListener implements ReaderEventListener {

    
private static final String LAZY_INIT = "true";

    @Override
    
public void defaultsRegistered(DefaultsDefinition defaultsDefinition) {
        
//set lazy init true
        if (defaultsDefinition instanceof DocumentDefaultsDefinition) {
            DocumentDefaultsDefinition defaults 
= (DocumentDefaultsDefinition) defaultsDefinition;
            defaults.setLazyInit(LAZY_INIT);
        }
    }

    @Override
    
public void aliasRegistered(AliasDefinition aliasDefinition) {
        
//no-op
    }

    @Override
    
public void componentRegistered(ComponentDefinition componentDefinition) {
        
//no-op
    }

    @Override
    
public void importProcessed(ImportDefinition importDefinition) {
        
//no-op
    }

}

LazyInitClasspathXmlApplicationContext.java (复写AbstractXmlApplicationContext,创建XmlBeanDefinitionReader的时候注入LazyInitListener)
 1 public class LazyInitClasspathXmlApplicationContext extends ClassPathXmlApplicationContext {
 2 
 3     public LazyInitClasspathXmlApplicationContext(String location) {
 4         super(location);
 5     }
 6 
 7     @Override
 8     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
 9         // Create a new XmlBeanDefinitionReader for the given BeanFactory.
10         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
11 
12         // Configure the bean definition reader with this context's
13         // resource loading environment.
14         beanDefinitionReader.setResourceLoader(this);
15         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
16 
17         // 添加的代码,设置LazyInitListener
18         beanDefinitionReader.setEventListener(new LazyInitListener());
19 
20         // Allow a subclass to provide custom initialization of the reader,
21         // then proceed with actually loading the bean definitions.
22         initBeanDefinitionReader(beanDefinitionReader);
23         loadBeanDefinitions(beanDefinitionReader);
24     }
25 
26 }

演示代码如下:
TestBean:一个Spring Bean对象
public class TestBean {

    
public void init() {
        
try {
            Thread.sleep(
5000);
        } 
catch (InterruptedException e) {
            
//ignore
            System.out.println(e);
        }
        System.out.println(
"TestBean Init");
    }
}

Spring配置文件:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
5 
6     <bean id="testBean" class="com.alibaba.javalab.spring.lazy.TestBean" init-method="init" />
7 </beans>

测试代码:
 1 public class Run {
 2 
 3     private static final String CONFIG = "classpath:spring/bean.xml";
 4 
 5     public static void main(String[] args) {
 6         testInit();
 7         System.out.println("===============================");
 8         testLazyInit();
 9     }
10 
11     public static void testInit() {
12         new ClassPathXmlApplicationContext(CONFIG);
13     }
14 
15     public static void testLazyInit() {
16         new LazyInitClasspathXmlApplicationContext(CONFIG);
17     }
18 }


大功告成.收工. :)

posted on 2010-06-03 12:46 stone2083 阅读(3155) 评论(2)  编辑  收藏 所属分类: java

Feedback

# re: 编程方式实现SpringBean LazyInit 2010-06-19 01:11 stone2083

今天又细看了代码,发现覆写
AbstractXmlApplicationContext.initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) 方法更为合理.  回复  更多评论   

# re: 编程方式实现SpringBean LazyInit[未登录] 2010-07-07 15:26 zhao

你好强啊!  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航: