伯樂線上注:本文來自文章作者張開濤的推薦投稿(原文)。如果其他朋友也有不錯的原創或譯文,可以嘗試推薦給伯樂線上。
————————————————————
在除錯spring應用時,動輒幾十秒,甚至有的應用上分鐘的啟動速度,會讓整個除錯速度慢下來了。等待時間讓人抓狂。不知道大家是如何加速spring應用除錯速度的,在此分享下我的一次加速過程。歡迎補充指正。
環境
配置:
1 2 3 4 5 6 |
thinkpad t410 記憶體:4G記憶體 CPU:Intel P8700 雙核2.53GHZ 系統:WIN XP 開發工具:Intellij IDEA 12.0.4 Maven + spring3.2.3 + hibernate4.2.2+Spring data jpa 1.3.1 |
未優化前spring容器啟動速度:
16890毫秒 =(14609毫秒(ContextLoaderListener載入的)+2281毫秒(Springmvc載入的)
優化後spring容器啟動速度:
7797毫秒 =(6563毫秒(ContextLoaderListener載入的)+1234毫秒(Springmvc載入的)
速度提升了一半多,而且以後在除錯階段,大部分就停留在這個時間左右。
注意:此處只是spring容器啟動速度,不包括伺服器啟動時的速度。因為我的系統好久沒清理了,否則可能速度會更快。
加速Spring
1、掃描註解Bean
寫比較精確的掃描路徑,如掃描@Service和@Repository:
1 |
<context:component-scan base-package="com.sishuok.es.**.repository,com.sishuok.es.**.service,com.sishuok.es.**.extra"> |
這樣寫,比直接寫com.sishuok.es速度要快很多,因為這樣掃描的class會很少。
還有,如springmvc 掃描:
1 |
<context:component-scan base-package="com.sishuok.es.**.web.controller" use-default-filters="false"> |
此處只掃描專案的web.controller包,這樣掃描的class也很少。
還有如事務的掃描:
1 |
execution(* com.sishuok.es..service..*+.*(..) |
還有如使用spring data jpa時也是這樣:
1 2 |
<jpa:repositories base-package="com.sishuok.es.**.repository" |
這裡需要大家有良好的分包,否則無法優化。
2、延遲載入你的bean
常見的方式是在配置檔案中在<beans>上加:
1 |
default-lazy-init="true" |
2.1、這種方式只對xml宣告的bean有效;
2.2、註解掃描的bean無效,如@Service,需要使用@Lazy指定,但這樣太麻煩,需要一個一個的配置;
2.3、還有就是如果你使用springmvc,lazy-init幾乎沒啥用,因為springmvc容器在啟動時會通過DefaultAnnotationHandlerMapping查詢相關的帶有@RequestMapping的bean並註冊請求對映;所以相關的如Service/Repository也級聯非lazy-init;
因此我寫了個工具:SpeedUpSpringProcessor,其作用是:lazy-init所有bean,包括註解的bean;對於【2.3】後續介紹解決方案;具體配置請參考最後。
3、移除除錯階段不相干的bean
有些bean在除錯階段我們並不需要,如我們在測試使用者模組時,可能不需要測試許可權模組;此時我們可以把不相干的bean移除掉;具體配置請參考最後。
這樣的話,可以考慮如把@Controller的bean移除,這樣的話如Service/Repository就可以lazy-init了。
常見的可以移除的如:
任務排程器(quartz)、AOP相關等等;
此處需要合理的分包,否則無法應用或應用困難。
4、刪除無用屬性
如在測試shiro時,可能不需要remember的功能,此時可以把屬性移除/禁用(即將值設定為false);具體配置請參考最後。
5、替換正式機資料來源為最快的資料來源
如此處我把DruidDataSource資料來源直接替換為org.springframework.jdbc.datasource.DriverManagerDataSource,這個速度最快;
6、替換jackson為fastjson
此處測試了下jackson速度比fastjson慢許多的。支援國產。
7、專案分模組開發
如果專案模組比較多,可以考慮放棄註解,而使用xml配置方式+約定。
因為實際做專案時可能把配置分到多個配置檔案,此時我嘗試了下合併到一個,幾乎沒啥速度提升,所以還是分開存好。
到此spring容器啟動速度算是比較快了,不知道大家還有沒有好的策略。歡迎指點。
加速Hibernate/JPA
此處以org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean為例。
1、 精確化packagesToScan
和之前的spring一樣,寫比較精確的實體掃描路徑
1 |
<property name="packagesToScan" value="com.sishuok.es.**.entity"/> |
2、generateDdl=false 禁用掉
沒必要每次都生成ddl
3、 禁用JSR-303驗證
預設情況下是AUTO,會根據classpath下是否有jsr-303實現來自動註冊;
1 2 3 4 5 |
<!-- 使用自定義的validator進行jsr303驗證 --> <entry key="javax.persistence.validation.factory" value-ref="validator"/> <!-- jsr303驗證模式 因為其要麼驗證 要麼不驗證 不能按照規則走 所以此處禁用 --> <!-- #http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/configuration.html --> <entry key="javax.persistence.validation.mode" value="NONE"/> |
此處validator 直接引用我們專案中定義的,而不是讓hibernate再去new一個。而且也不推薦在這驗證,具體為什麼,請參考我的《我是這樣認識註解和XML的》。
4、如果你的專案都是註解,此時就沒必要掃描hbm了,禁用掉
1 |
<entry key="hibernate.archive.autodetection" value="class"/> |
5、如果你不使用NamedQuery,禁用掉
1 |
<entry key="hibernate.query.startup_check" value="false"/> |
6、在除錯階段禁用掉二級快取
通過如上手段,我的spring容器啟動速度提升了一半多。大家還有好的優化策略嗎?如果有歡迎補充。具體配置請參考最後。
之前提到的SpeedUpSpringProcessor配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
<!-- 優化spring啟動 通過移除bean定義 和 lazy init 實現 --> <bean class="com.sishuok.es.common.spring.SpeedUpSpringProcessor"> <!-- 需要從bean定義中移除的bean的名字 --> <property name="removedBeanNames"> <list> <!-- spring-config-quartz.xml --> <value>scheduler</value> <value>autoClearDeletedRelationTrigger</value> <value>autoClearExpiredOrDeletedmMessageTrigger</value> <value>autoClearDeletedRelationJob</value> <value>autoClearExpiredOrDeletedmMessageJob</value> <!-- spring-config-shiro.xml --> <value>rememberMeCookie</value> <value>rememberMeManager</value> <value>shiroCacheManager</value> <value>sessionValidationScheduler</value> <value>sessionValidationScheduler</value> <!-- spring-mvc.xml --> <value>multipartResolver</value> <!-- spring-config-monitor.xml --> <value>druidStatInterceptor</value> <value>druidAdvisor</value> </list> </property> <!-- 需要從bean定義中移除的bean的屬性 --> <!--替換掉的屬性值 see removedBeanProperties 只支援簡單屬性--> <property name="removeOrReplaceBeanProperties"> <list> <!-- spring-config-shiro.xml --> <value>sessionManager#cacheManager</value> <value>sessionManager#cacheManager</value> <value>sessionManager#sessionValidationScheduler</value> <value>securityManager#rememberMeManager</value> <!-- spring-config.xml --> <value>entityManagerFactory#jpaPropertyMap#hibernate.default_batch_fetch_size"</value> <value>entityManagerFactory#jpaPropertyMap#hibernate.max_fetch_depth"</value> <value>entityManagerFactory#jpaPropertyMap#hibernate.generate_statistics</value> <value>entityManagerFactory#jpaPropertyMap#hibernate.bytecode.use_reflection_optimizer</value> <value>entityManagerFactory#jpaPropertyMap#hibernate.cache.use_second_level_cache=false</value> <value>entityManagerFactory#jpaPropertyMap#hibernate.cache.use_query_cache</value> <value>entityManagerFactory#jpaPropertyMap#hibernate.cache.region.factory_class</value> <value>entityManagerFactory#jpaPropertyMap#hibernate.cache.use_structured_entries</value> <value>entityManagerFactory#jpaPropertyMap#net.sf.ehcache.configurationResourceName</value> </list> </property> <!-- 需要從bean定義中移除指定的類型別 正規表示式--> <property name="removedClassPatterns"> <list> <value>com\.sishuok\.es\.showcase.*</value> <value>com\.sishuok\.es\.monitor.*</value> <value>com\.sishuok\.es\.extra\.aop.*</value> <value>com\.sishuok\.es\.extra\.quartz.*</value> <value>com\.sishuok\.es\.conf.*</value> <!--<value>com\.sishuok\.es\.personal.*\.web\.controller.*</value>--> <!--<value>com\.sishuok\.es\.sys.*\.web\.controller.*</value>--> </list> </property> <!-- 指定非延遲載入的bean--> <property name="noneLazyBeanNames"> <list> <value>domainClassConverter</value> </list> </property> </bean> |
1、預設所有bean lazy-init;
2、removedClassPatterns:正規表示式,即可以移除的bean的class路徑模式,bean class匹配該模式的將移除;此處需要良好的分包,否則不好應用;
3、removedBeanNames:即在除錯期間可以移除的bean;
4、removeOrReplaceBeanProperties:除錯期間可以刪除/替換掉的bean屬性;
如移除shiro的sessionManager的cacheManager;
如禁用hibernate二級快取:entityManagerFactory#jpaPropertyMap#hibernate.cache.use_second_level_cache=false
5、noneLazyBeanNames:有些bean不能lazy-init;排除掉。
具體實現請參考:
可以直接下載使用。
SpeedUpSpringProcessor:
spring-speed-up.xml:
https://github.com/zhangkaitao/es/blob/master/web/src/main/resources/spring-speed-up.xml
其他提到的配置檔案都在:
https://github.com/zhangkaitao/es/tree/master/web/src/main/resources
開啟/關閉:
此處我使用了spring的profile:
1 |
<beans profile="development" > |
即只有當System.getProperties中有spring.profiles.active=developement才執行除錯模式,所以如果沒有該配置還是走的正常流程,對系統沒有影響,所以此處大家可以使用:
1、jetty內嵌執行時設定該屬性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>${jetty.version}</version> <configuration> ---省略 <!-- spring profile --> <systemProperties> <systemProperty> <name>spring.profiles.active</name> <value>development</value> </systemProperty> </systemProperties> </configuration> </plugin> |
2、寫多個bat檔案分別執行不同的情況。