背景:
工程单元测试希望和生产环境应用共用一份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.
接口代码如下:
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对象,见:
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)
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)
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 void init() {
try {
Thread.sleep( 5000 );
} catch (InterruptedException e) {
// ignore
System.out.println(e);
}
System.out.println( " TestBean Init " );
}
}
Spring配置爱之谷文件:
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 >
测试代码:
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 }
大功告成.收工. :)