簡單分析springmvc是如何解析view視圖,並返回頁面給前端
SpringMVC配置視圖解析器
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property key="prefix" value="/WEB-INF/jsp/"/>
<property key="suffix" value=".jsp" />
</bean>
配置的為jsp的解析器
ViewResolver接口
其內部只有一個接口方法,具體如下
View resolveViewName(String viewName, Locale locale) throws Exception;
由上可知其是通過解析ViewName來得到View視圖對象
ViewResolver實現類-BeanNameViewResolver
其意圖是參數viewName就是springmvc上下文中的beanName對象,具體源碼如下
@Override
public View resolveViewName(String viewName, Locale locale) throws BeansException {
//獲得springmvc上下文
ApplicationContext context = getApplicationContext();
if (!context.containsBean(viewName)) {
//viewName不存在,則直接返回null
return null;
}
if (!context.isTypeMatch(viewName, View.class)) {
//viewName對應的beanName不是View.class的實現類,則直接返回
return null;
}
return context.getBean(viewName, View.class);
}
BeanNameResolver表示返回的viewName必須是springmvc上下文中的beanName並且對應的實體類必須是View.class的實現類,否則返回null
ViewResolver實現類-AbstractCachingViewResolver
其是我們常用解析器的抽象類,比如Freemarker/Groovy,我們直接去看其實現的接口方法,具體源碼如下
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
//不采用緩存方案,則每次都進行創建View
if (!isCache()) {
return createView(viewName, locale);
}
else {
//此處為使用緩存情況下的獲取View對象
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) {
//默認會返回一個null的View對象
view = UNRESOLVED_VIEW;
}
if (view != null) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
接以上代碼我們接着分析AbstractCachingViewResolver#createView()方法
protected View createView(String viewName, Locale locale) throws Exception {
//此處的loadView便是模板方法,供子類去實現
return loadView(viewName, locale);
}
此處我們只分析其某個實現類UrlBasedViewResolver
UrlBasedViewResolver-對應請求url的解析器
- 常用的內部屬性
public static final String REDIRECT_URL_PREFIX = "redirect:";
public static final String FORWARD_URL_PREFIX = "forward:";
//設置前綴
private String prefix = "";
//設置后綴
private String suffix = "";
//設置指定的viewName集合
private String[] viewNames ;
- createView()復寫父類方法,即在創建view對象前做下跳轉的請求檢查
@Override
protected View createView(String viewName, Locale locale) throws Exception {
//viewNames集合為null或者對應的viewName在viewNames集合內則返回true
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
//檢查handler返回的值為string類型時是否包含"redirect:"前綴
//此處處理的便是跳轉請求,比如"redirect:/user/list"
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
return applyLifecycleMethods(viewName, view);
}
// Check for special "forward:" prefix.
//同"redirect:"請求,此處為服務端直接跳轉
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
//通過父類再去調用loadView()方法,其實是本類也復寫了此方法
return super.createView(viewName, locale);
}
- loadView()復寫方法
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
//創建View對象
AbstractUrlBasedView view = buildView(viewName);
//將view對象與viewName綁定注冊至springmvc上下文中
View result = applyLifecycleMethods(viewName, view);
return (view.checkResource(locale) ? result : null);
}
- buildView()創建View對象主邏輯
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//獲取類似FreemarkerView.class/GroovyView.class
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
//設置view對應的資源路徑,此處便可知我們設置prefix和suffix的作用
view.setUrl(getPrefix() + viewName + getSuffix());
//下面都是設置與UrlBasedViewResolver的相關內部屬性
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
//是否暴露路徑變量
Boolean exposePathVariables = getExposePathVariables();
if (exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
//是否暴露springmvc的bean對象作為屬性使用
Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
if (exposeContextBeansAsAttributes != null) {
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
}
String[] exposedContextBeanNames = getExposedContextBeanNames();
if (exposedContextBeanNames != null) {
view.setExposedContextBeanNames(exposedContextBeanNames);
}
return view;
}
主要作用是設置prefix和suffix參數以及對應的內部屬性,可自行查閱,並通過
buildView()方法創建ViewClass屬性指定的View對象
AbstractTemplateViewResolver-UrlBasedViewResolver子類
添加另外的屬性
- 內部屬性
//是否暴露request對象的attributes屬性給前端引擎
private boolean exposeRequestAttributes = false;
//是否允許請求處理過程中復寫request對象的attributes
private boolean allowRequestOverride = false;
//是否暴露session對象的attributes屬性給前端引擎
private boolean exposeSessionAttributes = false;
//是否允許請求處理過程中復寫session對象的attributes
private boolean allowSessionOverride = false;
//是否使用暴露springMacroRequestContext屬性
private boolean exposeSpringMacroHelpers = true;
- buildView()復寫方法,主要是額外添加以上的屬性
@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//先調用父類的創建方法創建View對象
AbstractTemplateView view = (AbstractTemplateView) super.buildView(viewName);
//設置AbstractTemplateView的內部屬性
view.setExposeRequestAttributes(this.exposeRequestAttributes);
view.setAllowRequestOverride(this.allowRequestOverride);
view.setExposeSessionAttributes(this.exposeSessionAttributes);
view.setAllowSessionOverride(this.allowSessionOverride);
view.setExposeSpringMacroHelpers(this.exposeSpringMacroHelpers);
return view;
}
在UrlBasedViewResolver的基礎上設置額外的屬性,屬性集合見上文
FreeMarkerViewResolver-AbstractTemplateViewResolver子類
為方便理解,我們選取常用的模板引擎Freemarker,其他的引擎工具則供讀者自行分析
- 構造函數-設置viewClass,滿足上述的模板方法的viewClass的獲取
public FreeMarkerViewResolver() {
//設置的為FreemarkView.class
setViewClass(requiredViewClass());
}
小結
本節只解析了ViewResolver的簡單邏輯,其根據配置的ViewClass屬性,將配置的其他屬性都設置到ViewClass對應的實例中,具體的關於視圖的渲染,也就是
view#render()方法我們放在下節講解
