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 }
大功告成.收工. :)