freemarker小貼士-springmvc+freemarker視圖渲染的兩級緩存

    開發springmvc+freemarker項目時經常要調試頁面模版的元素,爲了使修改立即生效,就得關閉緩存,否則視圖渲染一直走得緩存。關閉緩存就要注意了,如果使用FreeMarkerViewResolver視圖解析器(freemarker模版的解析器),則需要關閉兩處緩存。

   第一級緩存,FreeMarkerViewResolver的視圖緩存(viewAccessCache)。這級緩存是springmvc的視圖緩存,mvc會將之前解析得到的view對象緩存到一個map裏面。當map的size達到一定數值時(默認是1024),則刪除第一個元素。想要關閉這級緩存只需要將緩存的限制值(cacheLimit)爲0即可。

<!-- Spring MVC頁面層FreeMarker的處理類 -->
<bean id="freeMarkerViewResolver"
    class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
    <property name="viewClass"
        value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
    <property name="cacheLimit" value="0" />
</bean>

springmvc用了巧妙的方式來保證map的size不超過cacheLimit。mvc維護了兩個map,一個是viewAccessCache,另一個是viewCreationCache。viewAccessCache用於訪問,viewCreationCache用於創建。mvc怎麼保證map的size不超過cacheLimit呢?它在創建viewCreationCache時重載了removeEldestEntry方法,viewCreationCache添加元素後當map的size > cacheLimit時就會刪除viewAccessCache裏面對應key的view對象,並返回true從而使viewCreationCache也刪除自己保存的對象。

public abstract class AbstractCachingViewResolver 
extends WebApplicationObjectSupport implements ViewResolver {
    ......

	/** Fast access cache for Views, returning already cached instances without a global lock */
	private final Map<Object, View> viewAccessCache = 
                new ConcurrentHashMap<Object, View>(DEFAULT_CACHE_LIMIT);

	/** Map from view key to View instance, synchronized for View creation */
	@SuppressWarnings("serial")
	private final Map<Object, View> viewCreationCache =
			new LinkedHashMap<Object, View>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
				@Override
				protected boolean removeEldestEntry(Map.Entry<Object, View> eldest) {
					if (size() > getCacheLimit()) {
						viewAccessCache.remove(eldest.getKey());
						return true;
					}
					else {
						return false;
					}
				}
			};
    ......
}

viewReslver解析view的時候判斷有沒有開啓緩存(cacheLimit > 0時isCache就會返回true)。如果有就去獲取viewAccessCache是否有緩存,有則返回。如果viewAccessCache沒有就去創建view對象,創建成功後把view對象put到viewAccessCache和viewCreationCache。注意viewCreationCache重載了方法removeEldestEntry,put對象進去就會觸發該方法刪除viewAccessCache(當size > cacheLimit時),同時返回true從而使viewCreationCache也刪除自己維護的對象。

public abstract class AbstractCachingViewResolver 
extends WebApplicationObjectSupport implements ViewResolver {
    ......
	@Override
	public View resolveViewName(String viewName, Locale locale) throws Exception {
		//筆者注:cacheLimit > 0 isCache返回true
        if (!isCache()) {
			return createView(viewName, locale);
		} else {
			Object cacheKey = getCacheKey(viewName, locale);
            //筆者注:判斷viewAccessCache裏面有沒有,沒有則去創建流程
			View view = this.viewAccessCache.get(cacheKey);
			if (view == null) {
				synchronized (this.viewCreationCache) {
					view = this.viewCreationCache.get(cacheKey);
					if (view == null) {
						// Ask the subclass to create the View object.
						view = createView(viewName, locale);
						......
						if (view != null) {
							this.viewAccessCache.put(cacheKey, view);
                            //筆者注:put就會觸發上面重載的方法(removeEldestEntry)
							this.viewCreationCache.put(cacheKey, view);
				            ......
						}
					}
				}
			}
			return (view != UNRESOLVED_VIEW ? view : null);
		}
	}
    ......
}

      第二級緩存,模版緩存(TemplateCache)。繞過FreeMarkerViewResolverviewResolver的緩存後,會來到freemarker加載模版(ftl文件)的邏輯,這個過程中同樣也用到了緩存。這級緩存是用時間控制的,緩存中模版加載的時間超過delay則會重新加載模版文件。想要關閉這級緩存需要將delay(配置項爲template_update_delay)時間設置爲0,這樣相當於設置模版緩存的有效期是0s。

<bean id="freemarkerConfiguration"
    class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">    
    <property name="freemarkerSettings">
        <props>
            <!-- 配置緩存時間 -->
            <prop key="template_update_delay">0</prop>
        </props>
    </property>
</bean>

    總結一下,調試mvc要想頁面實時生效,需要關閉兩個緩存:

  1. FreeMarkerViewResolver的cacheLimit設置爲0
  2. freemarkerSettings的template_update_delay也要設置爲0(單位s)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章