最近在負責一個大項目,項目組成員包括項目經理大概10個人左右。項目技術用struts+spring+hibernate實現。項目的規
模相對來說是比較大的,總共有10大模塊,每個大模塊又分為有十幾個、甚至幾十個小模塊。開發工具用eclipse,由于在
開發階段,項目開發成員需要頻繁重啟服務器。在啟動服務器的時候,每次啟動時間總是會超過1分鐘。記得以前在做另外
一個項目時,啟動時間不到5秒鐘,相差了10倍,而且項目規模是差不多的。

從初步分析來說,應該是hibernate解釋hbm.xml時花費時間,或者可能是spring容器啟動并解釋所有的bean配置文件 。診
斷了一下,發現1分鐘消耗的時間主要分布在hibernate解釋hbm.xml花費5秒;spring容器從啟動到解釋bean配置文件竟然
花了58秒,真是太囂張了。當時非常懷疑spring的效率問題。企圖從網上搜索相關資料,看看有什么優化措施。

首先是找到了hibernate的啟動優化 http://www.hibernate.org/194.html  里面的主要思想是通過將xml序列花到本地的
文件里,每次讀取的時候根據情況,從本地文件讀取并反序列化,節省了hibernate xml的解析時間。按照這個方式測試了
一下,發現hibernate的啟動時間從5秒降低到3秒,但是這個優化對于整個啟動過程是杯水車薪的,毫無用處。
下面是hibernate的優化代碼:

public Configuration addCachableFile(String xmlFile) throws MappingException {        
        
try {
            File file 
= new File(xmlFile);
            File lazyfile 
= new File(xmlFile + ".bin");
            org.dom4j.Document doc 
= null
            List errors 
= new ArrayList();
            
if(file.exists() && lazyfile.exists() && file.lastModified()<lazyfile.lastModified()) {
                log.info(
"Mapping lazy file: " + lazyfile.getPath());
                ObjectInputStream oip 
= null;
                oip 
= new ObjectInputStream(new FileInputStream(lazyfile));
                doc 
= (org.dom4j.Document) oip.readObject();
                oip.close(); 
            }
 else {
                doc 
= xmlHelper.createSAXReader(xmlFile, errors, entityResolver).read( file );
                log.info(
"Writing lazy file to " + lazyfile);
                ObjectOutputStream oup 
= new ObjectOutputStream(new FileOutputStream(lazyfile));
                oup.writeObject(doc);
                oup.flush();
                oup.close();
            }

            
            
if ( errors.size()!=0 ) throw new MappingException( "invalid mapping", (Throwable) errors.get(0) );
            add(doc);
            
return this;
        }

        
catch (Exception e) {
            log.error(
"Could not configure datastore from file: " + xmlFile, e);
            
throw new MappingException(e);
        }

    }

沒辦法,又仔細查看了spring的資料,終于發現spring的容器是提供了lazy-load的,即默認的缺省設置是bean沒有lazy- load,
該屬性處于false狀態,這樣導致spring在啟動過程導致在啟動時候,會默認加載整個對象實例圖,從初始化ACTION配置、
到 service配置到dao配置、乃至到數據庫連接、事務等等。這么龐大的規模,難怪spring的啟動時間要花將近1分鐘。嘗
試了一下,把beans的 default-lazy-init改為true就,再次啟動,速度從原來的55秒,降到8秒鐘!!Great!雖然是非常
小一個改動,但是影響確實非常大。一個項目組10個人,假若每個人一天平均需要在eclipse下啟動測試服務器50次。那么
一天項目組需要重啟500次,每次節省50秒的話,就是 25000秒,將近幾個小時,差不多一個工作日,多么可觀的數字!

 
不過在運行期間第一次點頁面的時候,由于spring做了lazy-load,現在就需要啟動一部分需要的beans,所以稍微慢2-3
 秒鐘,但是明顯比等幾十秒要快很多,值得一鑒。

 以上是針對開發階段的spring容器啟動優化,在部署到實際環境中,倒是沒必要設置為lazy-load。畢竟部署到實際環境
 中不是經常的事,每次啟動1分鐘倒不是大問題。

我這里要提醒的是不是說有的beans都能設置default-lazy-init成為true.對于scheduler的bean不能用lazy-init

< beans  default-lazy-init ="true" >   
     
< bean  class ="org.springframework.scheduling.quartz.SchedulerFactoryBean" > 
         
< property  name ="triggers" > 
             
< list > 
                 
< ref  bean ="buildHtmlTrigger" /> 
                 
< ref  bean ="askTrigger" /> 
                 
< ref  bean ="mailSenderTrigger" /> 
                 
< ref  bean ="topicDetailBuildTrigger" /> 
                 
< ref  bean ="forumBuildTrigger" /> 
                 
< ref  bean ="topicBuildTrigger" /> 
             
</ list > 
         
</ property > 
     
</ bean > 
</ beans > 
這樣的話。所有的scheduler就都不管用了。所以請大家要注意。下面這樣才是可以執行的
< beans >  
     
< bean  class ="org.springframework.scheduling.quartz.SchedulerFactoryBean" > 
         
< property  name ="triggers" > 
             
< list > 
                 
< ref  bean ="buildHtmlTrigger" /> 
                 
< ref  bean ="askTrigger" /> 
                 
< ref  bean ="mailSenderTrigger" /> 
                 
< ref  bean ="topicDetailBuildTrigger" /> 
                 
< ref  bean ="forumBuildTrigger" /> 
                 
< ref  bean ="topicBuildTrigger" /> 
             
</ list > 
         
</ property > 
     
</ bean > 
</ beans >