之前web View一直用的JSP,现在使用的velocity。
通过Insight Velocity配置,说说SpringMVC view生成的过程。
......
1.Velocity 配置解析
需要配置VelocityConfigurer,这是个VelocityEngineFactory,用于初始化自定义VelocityEngine。
需要配置VelocityLayoutViewResolver,用于生成对应的VelocityView。
特别的,如果不配置ViewResolver的情况下,springmvc 会默认采用InternalResourceViewResolver,实现jsp支持。
2.View 的生成过程
直接看实现:
/**
* 委托buildView 方法来创建指定的View instance (VelocityView/FreeMarkerView/JstlView...)
* 再经过beanFactory进行初始化处理。
*/
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
// 非常重要,将View 纳入bean生命周期进行管理
View result = applyLifecycleMethods(viewName, view);
return (view.checkResource(locale) ? result : null);
}
/**
* Creates a new View instance.
* buildView基础实现,不同View class自定义属性在subClass.buildView中实现.
*/
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
// 配置的prefix + controller.viewname + suffix
view.setUrl(getPrefix() + viewName + getSuffix());
// ...
return view;
}
@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
VelocityView view = (VelocityView) super.buildView(viewName);
// 配置dateToolAttribute、numberToolAttribute,如果不为null,那么会在Velocity context 加入Date、Number Helper
view.setDateToolAttribute(this.dateToolAttribute);
view.setNumberToolAttribute(this.numberToolAttribute);
if (this.toolboxConfigLocation != null) {
((VelocityToolboxView) view).setToolboxConfigLocation(this.toolboxConfigLocation);
}
return view;
}
/**
* 如上所见,ViewResolver.loadView过程中会进行View生命周期管理.
* Looks for a single VelocityConfig bean to find the relevant VelocityEngine for this factory.
*/
@Override
protected void initApplicationContext() throws BeansException {
super.initApplicationContext();
if (getVelocityEngine() == null) {
// try to autodetect one. 工程配置的VelocityConfigurer,得到velocityEngine bean.
setVelocityEngine(autodetectVelocityEngine());
}
}
protected void exposeToolAttributes(Context velocityContext, HttpServletRequest request) throws Exception {
// ...
// 如ViewResolver配置,根据dateToolAttribute、numberToolAttribute确定是否加入Date/Number Helper
if (this.dateToolAttribute != null || this.numberToolAttribute != null) {
Locale locale = RequestContextUtils.getLocale(request);
if (this.dateToolAttribute != null) {
velocityContext.put(this.dateToolAttribute, new LocaleAwareDateTool(locale));
}
if (this.numberToolAttribute != null) {
velocityContext.put(this.numberToolAttribute, new LocaleAwareNumberTool(locale));
}
}
}
3.设计的亮点-cache
自定义的ViewResolver 都是基于AbstractCachingViewResolver,顾名思义,有缓存功能的基类。
在这个类中,可以看到LinkedHashMap FIFO策略缓存的实现。
也可以看到双重同步锁实现多线程单例实现。上源码:
/** 直接从缓存中快速获取view 实例,非全局锁 */
private final Map
viewAccessCache = new ConcurrentHashMap
(DEFAULT_CACHE_LIMIT);
/** 1.view 实例创建过程Cache FIFO控制 */
private final Map
viewCreationCache = new LinkedHashMap
(DEFAULT_CACHE_LIMIT, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry
eldest) { if (size() > getCacheLimit()) { viewAccessCache.remove(eldest.getKey()); return true; } else { return false; } } }; /** 2.双重同步锁实现view 实例多线程安全单例 */ public View resolveViewName(String viewName, Locale locale) throws Exception { if (!isCache()) { return createView(viewName, locale); } else { Object cacheKey = getCacheKey(viewName, locale); 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.cacheUnresolved) { view = UNRESOLVED_VIEW; } if (view != null) { this.viewAccessCache.put(cacheKey, view); this.viewCreationCache.put(cacheKey, view); } } } } return (view != UNRESOLVED_VIEW ? view : null); } }
简直就是教科书!!!