Tags:
Spring LazyInit DocumentDefaultsDefinition ReaderEventListener AbstractXmlApplicationContext
背景:
工程單元測試希望和生產(chǎn)環(huán)境應(yīng)用共用一份Spring配置文件.
生產(chǎn)環(huán)境應(yīng)用為了客戶體驗使用非LazyInit模式,但是單元測試下為了當(dāng)前測試提高響應(yīng)時間,希望設(shè)置LazyInit.
分析源代碼,得知,Spring在解析XML時,會將Bean默認(rèn)配置,放入到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是否提供了入口點,進(jìn)行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是在哪個入口點,提供了回調(diào).答案是XmlBeanDefinitionReader.
在AbstractXmlApplicationContext,創(chuàng)建了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 }
我們只需要去復(fù)寫這個方法,在創(chuàng)建XmlBeanDefinitionReader的時候,去注入EventListener即可.
擴(kuò)展代碼如下:
LazyInitListener.java (不管配置文件如何配置,設(shè)置默認(rèn)的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 (復(fù)寫AbstractXmlApplicationContext,創(chuàng)建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 // 添加的代碼,設(shè)置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 }
大功告成.收工. :)