加速Java應(yīng)用開發(fā)1—Spring調(diào)試啟動速度
在調(diào)試spring應(yīng)用時,動輒幾十秒,甚至有的應(yīng)用上分鐘的啟動速度,會讓整個調(diào)試速度慢下來了。等待時間讓人抓狂。不知道大家是如何加速spring應(yīng)用調(diào)試速度的,在此分享下我的一次加速過程。歡迎補充指正。
環(huán)境
配置:
- thinkpad t410
- 內(nèi)存:4G內(nèi)存
- CPU:Intel P8700 雙核2.53GHZ
- 系統(tǒng):WIN XP
- 開發(fā)工具:Intellij IDEA 12.0.4
- Maven + spring3.2.3 + hibernate4.2.2+Spring data jpa 1.3.1
未優(yōu)化前spring容器啟動速度:
16890毫秒 =(14609毫秒(ContextLoaderListener加載的)+2281毫秒(Springmvc加載的)
優(yōu)化后spring容器啟動速度:
7797毫秒 =(6563毫秒(ContextLoaderListener加載的)+1234毫秒(Springmvc加載的)
速度提升了一半多,而且以后在調(diào)試階段,大部分就停留在這個時間左右。
注意:此處只是spring容器啟動速度,不包括服務(wù)器啟動時的速度。因為我的系統(tǒng)好久沒清理了,否則可能速度會更快。
加速Spring
1、掃描注解Bean
寫比較精確的掃描路徑,如掃描@Service和@Repository:
- <context:component-scan base-package="com.sishuok.es.**.repository,com.sishuok.es.**.service,com.sishuok.es.**.extra">
這樣寫,比直接寫com.sishuok.es速度要快很多,因為這樣掃描的class會很少。
還有,如springmvc 掃描:
- <context:component-scan base-package="com.sishuok.es.**.web.controller" use-default-filters="false">
此處只掃描項目的web.controller包,這樣掃描的class也很少。
還有如事務(wù)的掃描:
- execution(* com.sishuok.es..service..*+.*(..)
還有如使用spring data jpa時也是這樣:
- <jpa:repositories
- base-package="com.sishuok.es.**.repository"
這里需要大家有良好的分包,否則無法優(yōu)化。
2、延遲加載你的bean
常見的方式是在配置文件中在<beans>上加:
- default-lazy-init="true"
2.1、這種方式只對xml聲明的bean有效;
2.2、注解掃描的bean無效,如@Service,需要使用@Lazy指定,但這樣太麻煩,需要一個一個的配置;
2.3、還有就是如果你使用springmvc,lazy-init幾乎沒啥用,因為springmvc容器在啟動時會通過 DefaultAnnotationHandlerMapping查找相關(guān)的帶有@RequestMapping的bean并注冊請求映射;所以相關(guān)的如 Service/Repository也級聯(lián)非lazy-init;
因此我寫了個工具:SpeedUpSpringProcessor,其作用是:lazy-init所有bean,包括注解的bean;對于【2.3】后續(xù)介紹解決方案;具體配置請參考***。
3、移除調(diào)試階段不相干的bean
有些bean在調(diào)試階段我們并不需要,如我們在測試用戶模塊時,可能不需要測試權(quán)限模塊;此時我們可以把不相干的bean移除掉;具體配置請參考***。
這樣的話,可以考慮如把@Controller的bean移除,這樣的話如Service/Repository就可以lazy-init了。
常見的可以移除的如:
- 任務(wù)調(diào)度器(quartz)、AOP相關(guān)等等;
- 此處需要合理的分包,否則無法應(yīng)用或應(yīng)用困難。
4、刪除無用屬性
如在測試shiro時,可能不需要remember的功能,此時可以把屬性移除/禁用(即將值設(shè)置為false);具體配置請參考***。
5、替換正式機數(shù)據(jù)源為最快的數(shù)據(jù)源
如此處我把DruidDataSource數(shù)據(jù)源直接替換為org.springframework.jdbc.datasource.DriverManagerDataSource,這個速度最快;
6、替換jackson為fastjson
此處測試了下jackson速度比fastjson慢許多的。支持國產(chǎn)。
7、項目分模塊開發(fā)
如果項目模塊比較多,可以考慮放棄注解,而使用xml配置方式+約定。因為實際做項目時可能把配置分到多個配置文件,此時我嘗試了下合并到一個,幾乎沒啥速度提升,所以還是分開存好。
到此spring容器啟動速度算是比較快了,不知道大家還有沒有好的策略。歡迎指點。
加速Hibernate/JPA
此處以org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean為例。
1、 精確化packagesToScan
和之前的spring一樣,寫比較精確的實體掃描路徑
- <property name="packagesToScan" value="com.sishuok.es.**.entity"/>
2、generateDdl=false 禁用掉
沒必要每次都生成ddl
3、 禁用JSR-303驗證
默認情況下是AUTO,會根據(jù)classpath下是否有jsr-303實現(xiàn)來自動注冊;
- <!-- 使用自定義的validator進行jsr303驗證 -->
- <entry key="javax.persistence.validation.factory" value-ref="validator"/>
- <!-- jsr303驗證模式 因為其要么驗證 要么不驗證 不能按照規(guī)則走 所以此處禁用 -->
- <!-- #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了,禁用掉
- <entry key="hibernate.archive.autodetection" value="class"/>
5、如果你不使用NamedQuery,禁用掉
- <entry key="hibernate.query.startup_check" value="false"/>
6、在調(diào)試階段禁用掉二級緩存
通過如上手段,我的spring容器啟動速度提升了一半多。大家還有好的優(yōu)化策略嗎?如果有歡迎補充。具體配置請參考***。
之前提到的SpeedUpSpringProcessor配置
- <!-- 優(yōu)化spring啟動 通過移除bean定義 和 lazy init 實現(xiàn) -->
- <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>
- 默認所有bean lazy-init;
- removedClassPatterns:正則表達式,即可以移除的bean的class路徑模式,bean class匹配該模式的將移除;此處需要良好的分包,否則不好應(yīng)用;
- removedBeanNames:即在調(diào)試期間可以移除的bean;
- removeOrReplaceBeanProperties:調(diào)試期間可以刪除/替換掉的bean屬性;如移除shiro的sessionManager的cacheManager;如禁用hibernate二級緩存:entityManagerFactory#jpaPropertyMap#hibernate.cache.use_second_level_cache=false
- noneLazyBeanNames:有些bean不能lazy-init;排除掉。
具體實現(xiàn)請參考:
可以直接下載使用。
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
開啟/關(guān)閉:
此處我使用了spring的profile:
- <beans profile="development" >
即只有當(dāng)System.getProperties中有spring.profiles.active=developement才執(zhí)行調(diào)試模式,所以如果沒有該配置還是走的正常流程,對系統(tǒng)沒有影響,所以此處大家可以使用:
1、jetty內(nèi)嵌執(zhí)行時設(shè)置該屬性
- <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文件分別執(zhí)行不同的情況。
原文鏈接:http://jinnianshilongnian.iteye.com/blog/1883013