深入剖析Spring Web源码(十三) - 处理器映射,处理器适配器以及处理器的实现 - 处理器的实现架构 - 简单控制器
2010-10-10 20:18
1006 查看
2.2.2.3 处理器的实现架构
作为总控制器的派遣器Servlet将得到的处理器传递给支持此处理器的处理器适配器,处理器适配器然后调用处理器中适当的处理器方法,最后返回处理结果给派遣器Serlvet。处理器架构中并没有简单的处理器接口定义,任何一个对象类型都可以成为处理器,每个类型的处理器都有一个对应的处理器适配器,用于将HTTP请求适配给一定类型的处理器。
处理器根据类型分为简单控制器,注解控制器和HTTP请求处理器。我们在讨论基于流程的实现的小节中,已经对每种类型中的典型实现类进行了剖析。下面我们就对不同类型的处理器进行详细的剖析。
4.2.2.3.1 简单控制器
简单控制器是最常用的处理器类型,它有一个简单的接口定义,接口有唯一的处理器方法,方法接受HTTP请求对象和HTTP响应对象作为参数,并且返回模型和视图对象。当派遣器Servlet派遣一个HTTP请求到简单控制器处理器适配器,简单控制器处理器适配器就会传递HTTP请求对象和HTTP响应对象给简单控制器的处理器方法,控制器处理器方法调用服务层的业务逻辑处理方法后返回模型和视图对象,模型和视图对象最后返回给派遣器Servlet。简单控制器接口有很多抽象的或者具体的实现类,每个层次的类都实现一个独立的逻辑功能。如下类图所示,
图表 4‑31
从上图我们可以看到,简单控制器架构的实现比较复杂,它有很多的抽象实现类和具体的实现类,有些类之间互相独立,有些类是继承自其他类,设计和实现这些类有着不同的目的,实现不同的功能。下面通过功能对这些类实现进行分类。
l 抽象命令控制器(AbstractCommandController)
抽象命令控制器根据请求参数自动绑定一个命令类实例,子类实现根据绑定的命令类实例完成服务层的业务逻辑的调用后,最后决定跳转到某一个视图。它用来处理一个单一的直线式的流程。
l 抽象Form控制器(AbstractFormController)
抽象Form控制器也根据请求参数自动绑定一个命令类实例,它用来处理一个基于Form的流程,包括Form的显示,提交和取消。它是一个抽象类,但是它有不同的实现类实现不同的Form流程。下面我们具体介绍其三个实现类。
n 简单Form控制器(SimpleFormController)
简单的Form控制器用来处理一个具有两个状态的Form处理流程。他们是显示Form状态和提交Form状态。通过配置Form视图和成功视图,简单的Form控制器就可以开始工作了。第一次请求页面时,它使用HTTP GET协议,简单Form控制器自动显示Form视图。当Form提交的时候,它使用HTTP POST协议,简单Form控制器就进行服务层次的逻辑调用,最后显示成功视图。
图表 4‑32
n 可取消的Form控制器(CancellableFormController)
可取消的Form控制器是简单Form控制器的子类。它除了拥有显示Form状态和提交Form状态还增加了一个取消Form状态。如果一个Form提交中带有取消参数,则显示取消视图。
图表 4‑33
n 抽象导航控制器(AbstractWizardFormController)
抽象导航控制器更加复杂,它拥有更多的状态,它拥有一个或者多个页面状态,一个取消状态和一个完成状态。当一个Form加载时,它显示第一个页面,然后,通过页面提交的参数可以在不同的页面进行导航。当一个提交中包含取消参数或者完成参数,则显示取消视图或者完成视图。这是一个抽象类,子类可以根据业务需要在显示取消和完成试图前进行服务层次的业务逻辑的处理。
图表 4‑34
l 多动作控制器(MultiActionController)
多动作控制器是用于处理多个HTTP请求的处理器。它根据HTTP请求URL映射得到应该调用的处理器方法,通过反射调用处理器方法,并且封装返回结果作为模型和视图返回给简单控制器适配器。每个处理器方法可以有一个对应的最后修改方法,最后修改方法名是处理器方法名加上LastModified后缀构成的。最后修改方法也是通过反射调用并且返回结果的。
图表 4‑35
l Servlet相关控制器
Servlet相关控制器和简单Servlet处理器适配器功能类似,他们都实现将HTTP请求适配到一个已存的Servlet实现。但是,简单Servlet处理器适配器需要在Web应用程序环境中定义Servlet Bean,并且Servlet没有机会进行初始化和析构。
n Servlet包装控制器(ServletWrappingController)
Servlet包装控制器内部封装了一个Servlet实例,内部封装的Servlet实例对外并不开放,对于程序的其他范围是不可见的。封装的Servlet实例有机会进行初始化和析构。Servlet包装控制器适配所有的HTTP请求到内部封装的Servlet实例进行处理。它通常用于对已存Servlet的逻辑重用上。
n Servlet转发控制器(ServletForwardingController)
Servlet包装控制器将所有的HTTP请求转发给一个在web.xml中定义的Servlet。Web容器会对这个定义在web.xml的标准Servlet进行初始化和析构。
下面我们将用一个表格来总结Servlet封装相关对象异同。
Servlet封装对象 | 管理范围 | 初始化和析构 | 服务调用方式 |
SimpleServletHandlerAdaptor | Web应用程序环境 | 没有 | 直接 |
ServletWrappingController | 控制器内部 | 有 | 直接 |
ServletForwardingController | web.xml | 有 | Servlet派遣器 |
n URL文件名视图控制器(UrlFilenameViewController)
URL文件名视图控制器通过将URL翻译成为视图名,并且返回。
n 可参数化视图控制器(ParameterizableViewController)
可参数化视图控制器简单的返回配置的视图名。
在前面一些小结中,分析了基于不同流程的实现,我们已经对简单Form控制器及其父类的实现进行了深入剖析和代码注释,这里将只对其他类进行剖析和代码注释。
抽象命令控制器继承自基本命令控制器。前面小结分析到,基本命令控制器提供了创建命令对象和通过HTTP请求参数对命令对象进行绑定和校验的功能方法。抽象命令控制器使用这些方法实现一个线性的流程,首先创建命令对象,然后绑定校验命令对象,最后传递命令对象到抽象的方法进行业务逻辑的处理。子类应该根据业务逻辑实现此方法。如下代码所示,
public abstract class AbstractCommandController extends BaseCommandController {
public AbstractCommandController() {
}
public AbstractCommandController(Class commandClass) {
setCommandClass(commandClass);
}
public AbstractCommandController(Class commandClass, String commandName) {
setCommandClass(commandClass);
setCommandName(commandName);
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 根据命令类型,创建命令对象
Object command = getCommand(request);
// 绑定命令对象到HTTP请求参数,并且校验
ServletRequestDataBinder binder = bindAndValidate(request, command);
// 构造绑定结果对象
BindException errors = new BindException(binder.getBindingResult());
// 调用业务逻辑处理方法
return handle(request, response, command, errors);
}
// 子类根据业务逻辑实现此方法,它通常不适用于Form流程,它适用于一个简单的AJAX请求
protected abstract ModelAndView handle(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception;
}
可取消的Form控制器继承自简单的Form控制器。我们知道,简单Form控制器支持具有两种状态的Form流程,既显示Form视图状态和显示成功视图状态。可取消的Form控制器则增加了一个显示取消视图状态。如下代码所示,
public class CancellableFormController extends SimpleFormController {
// 缺省的代表取消请求的参数键值
private static final String PARAM_CANCEL = "_cancel";
private String cancelParamKey = PARAM_CANCEL;
private String cancelView;
public final void setCancelParamKey(String cancelParamKey) {
this.cancelParamKey = cancelParamKey;
}
public final String getCancelParamKey() {
return this.cancelParamKey;
}
// 当接收到一个取消请求,显示这个取消视图
public final void setCancelView(String cancelView) {
this.cancelView = cancelView;
}
public final String getCancelView() {
return this.cancelView;
}
@Override
protected boolean isFormSubmission(HttpServletRequest request) {
// 除了普通的Form提交请求,还包括具有取消参数的请求
return super.isFormSubmission(request) || isCancelRequest(request);
}
@Override
protected boolean suppressValidation(HttpServletRequest request, Object command) {
// 取消请求不需要校验参数
return super.suppressValidation(request, command) || isCancelRequest(request);
}
@Override
protected ModelAndView processFormSubmission(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception {
// 特殊处理取消请求
if (isCancelRequest(request)) {
return onCancel(request, response, command);
}
else {
return super.processFormSubmission(request, response, command, errors);
}
}
protected boolean isCancelRequest(HttpServletRequest request) {
// 存在取消参数,则是取消请求
return WebUtils.hasSubmitParameter(request, getCancelParamKey());
}
protected ModelAndView onCancel(HttpServletRequest request, HttpServletResponse response, Object command)
throws Exception {
// 代理到处理取消的处理器
return onCancel(command);
}
protected ModelAndView onCancel(Object command) throws Exception {
// 简单的返回取消视图
return new ModelAndView(getCancelView());
}
}
抽象导航控制器继承自抽象Form控制器。抽象Form控制器定义了两个状态,显示Form视图状态和显示成功视图状态。抽象导航控制器扩展了它的实现,拥有多个页面状态也一个取消视图状态和一个完成视图状态。它能够在不同的页面之间进行导航。它适用于一个模块有非常多的输入数据,以至于需要多个Tab页面进行输入。如下代码注释,
public abstract class AbstractWizardFormController extends AbstractFormController {
// 代表完成请求的参数
public static final String PARAM_FINISH = "_finish";
// 代表取消请求的参数
public static final String PARAM_CANCEL = "_cancel";
// 代表导航到一个新页面的参数
public static final String PARAM_TARGET = "_target";
// 代表当前的页面,这个参数也存在在request或者session里
public static final String PARAM_PAGE = "_page";
// 所有的页面视图
private String[] pages;
private String pageAttribute;
private boolean allowDirtyBack = true;
private boolean allowDirtyForward = false;
public AbstractWizardFormController() {
// AbstractFormController sets default cache seconds to 0.
super();
// Always needs session to keep data from all pages.
// 需要在Session中保存命令对象
setSessionForm(true);
// Never validate everything on binding ->
// wizards validate individual pages.
setValidateOnBinding(false);
}
public final void setPages(String[] pages) {
// 至少有一个页面,第一个页面表现作为传统的Form视图
if (pages == null || pages.length == 0) {
throw new IllegalArgumentException("No wizard pages defined");
}
this.pages = pages;
}
public final String[] getPages() {
return this.pages;
}
protected final int getPageCount() {
return this.pages.length;
}
public final void setPageAttribute(String pageAttribute) {
this.pageAttribute = pageAttribute;
}
public final String getPageAttribute() {
return this.pageAttribute;
}
public final void setAllowDirtyBack(boolean allowDirtyBack) {
this.allowDirtyBack = allowDirtyBack;
}
public final boolean isAllowDirtyBack() {
return this.allowDirtyBack;
}
public final void setAllowDirtyForward(boolean allowDirtyForward) {
this.allowDirtyForward = allowDirtyForward;
}
public final boolean isAllowDirtyForward() {
return this.allowDirtyForward;
}
@Override
protected final void onBindAndValidate(HttpServletRequest request, Object command, BindException errors)
throws Exception {
onBindAndValidate(request, command, errors, getCurrentPage(request));
}
// 针对不同的页面进行特殊的绑定和校验
protected void onBindAndValidate(HttpServletRequest request, Object command, BindException errors, int page)
throws Exception {
}
@Override
protected boolean isFormSubmission(HttpServletRequest request) {
// 除了Form提交请求,完成请求和取消请求都被视为Form提交
return super.isFormSubmission(request) || isFinishRequest(request) || isCancelRequest(request);
}
@Override
protected final Map referenceData(HttpServletRequest request, Object command, Errors errors)
throws Exception {
return referenceData(request, command, errors, getCurrentPage(request));
}
protected Map referenceData(HttpServletRequest request, Object command, Errors errors, int page)
throws Exception {
return referenceData(request, page);
}
// 针对不同的页面有不同的引用数据
protected Map referenceData(HttpServletRequest request, int page) throws Exception {
return null;
}
@Override
protected ModelAndView showForm(
HttpServletRequest request, HttpServletResponse response, BindException errors)
throws Exception {
// 第一次请求显示第一个页面,相当于传统的Form视图
return showPage(request, errors, getInitialPage(request, errors.getTarget()));
}
protected final ModelAndView showPage(HttpServletRequest request, BindException errors, int page)
throws Exception {
// 校验它是一个合法的页面
if (page >= 0 && page < getPageCount(request, errors.getTarget())) {
if (logger.isDebugEnabled()) {
logger.debug("Showing wizard page " + page + " for form bean '" + getCommandName() + "'");
}
// Set page session attribute, expose overriding request attribute.
Integer pageInteger = new Integer(page);
String pageAttrName = getPageSessionAttributeName(request);
if (isSessionForm()) {
if (logger.isDebugEnabled()) {
logger.debug("Setting page session attribute [" + pageAttrName + "] to: " + pageInteger);
}
request.getSession().setAttribute(pageAttrName, pageInteger);
}
request.setAttribute(pageAttrName, pageInteger);
// Set page request attribute for evaluation by views.
Map controlModel = new HashMap();
if (this.pageAttribute != null) {
controlModel.put(this.pageAttribute, new Integer(page));
}
// 取得具体某一页的视图名
String viewName = getViewName(request, errors.getTarget(), page);
// 显示视图
return showForm(request, errors, viewName, controlModel);
}
else {
throw new ServletException("Invalid wizard page number: " + page);
}
}
protected int getPageCount(HttpServletRequest request, Object command) {
return getPageCount();
}
// 每一个页面对应一个视图名,他们按照顺序存储
protected String getViewName(HttpServletRequest request, Object command, int page) {
return getPages()[page];
}
// 默认情况下,第一个页面就是初始化页面
protected int getInitialPage(HttpServletRequest request, Object command) {
return getInitialPage(request);
}
protected int getInitialPage(HttpServletRequest request) {
return 0;
}
protected String getPageSessionAttributeName(HttpServletRequest request) {
return getPageSessionAttributeName();
}
protected String getPageSessionAttributeName() {
return getClass().getName() + ".PAGE." + getCommandName();
}
@Override
protected ModelAndView handleInvalidSubmit(HttpServletRequest request, HttpServletResponse response)
throws Exception {
return showNewForm(request, response);
}
@Override
protected final ModelAndView processFormSubmission(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception {
int currentPage = getCurrentPage(request);
// Remove page session attribute, provide copy as request attribute.
String pageAttrName = getPageSessionAttributeName(request);
if (isSessionForm()) {
if (logger.isDebugEnabled()) {
logger.debug("Removing page session attribute [" + pageAttrName + "]");
}
request.getSession().removeAttribute(pageAttrName);
}
request.setAttribute(pageAttrName, new Integer(currentPage));
// cancel?
if (isCancelRequest(request)) {
if (logger.isDebugEnabled()) {
logger.debug("Cancelling wizard for form bean '" + getCommandName() + "'");
}
return processCancel(request, response, command, errors);
}
// finish?
if (isFinishRequest(request)) {
if (logger.isDebugEnabled()) {
logger.debug("Finishing wizard for form bean '" + getCommandName() + "'");
}
return validatePagesAndFinish(request, response, command, errors, currentPage);
}
// Normal submit: validate current page and show specified target page.
if (!suppressValidation(request, command, errors)) {
if (logger.isDebugEnabled()) {
logger.debug("Validating wizard page " + currentPage + " for form bean '" + getCommandName() + "'");
}
validatePage(command, errors, currentPage, false);
}
// Give subclasses a change to perform custom post-procession
// of the current page and its command object.
postProcessPage(request, command, errors, currentPage);
int targetPage = getTargetPage(request, command, errors, currentPage);
if (logger.isDebugEnabled()) {
logger.debug("Target page " + targetPage + " requested");
}
if (targetPage != currentPage) {
if (!errors.hasErrors() || (this.allowDirtyBack && targetPage < currentPage) ||
(this.allowDirtyForward && targetPage > currentPage)) {
// Allowed to go to target page.
return showPage(request, errors, targetPage);
}
}
// Show current page again.
return showPage(request, errors, currentPage);
}
protected int getCurrentPage(HttpServletRequest request) {
// Check for overriding attribute in request.
String pageAttrName = getPageSessionAttributeName(request);
Integer pageAttr = (Integer) request.getAttribute(pageAttrName);
if (pageAttr != null) {
return pageAttr.intValue();
}
// Check for explicit request parameter.
String pageParam = request.getParameter(PARAM_PAGE);
if (pageParam != null) {
return Integer.parseInt(pageParam);
}
// Check for original attribute in session.
if (isSessionForm()) {
pageAttr = (Integer) request.getSession().getAttribute(pageAttrName);
if (pageAttr != null) {
return pageAttr.intValue();
}
}
throw new IllegalStateException(
"Page attribute [" + pageAttrName + "] neither found in session nor in request");
}
protected boolean isFinishRequest(HttpServletRequest request) {
return WebUtils.hasSubmitParameter(request, PARAM_FINISH);
}
protected boolean isCancelRequest(HttpServletRequest request) {
return WebUtils.hasSubmitParameter(request, PARAM_CANCEL);
}
protected int getTargetPage(HttpServletRequest request, Object command, Errors errors, int currentPage) {
return getTargetPage(request, currentPage);
}
protected int getTargetPage(HttpServletRequest request, int currentPage) {
// 缺省情况下, _target参数代表要导航的目标页面
return WebUtils.getTargetPage(request, PARAM_TARGET, currentPage);
}
private ModelAndView validatePagesAndFinish(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors,
int currentPage) throws Exception {
// In case of binding errors -> show current page.
if (errors.hasErrors()) {
return showPage(request, errors, currentPage);
}
if (!suppressValidation(request, command, errors)) {
// In case of remaining errors on a page -> show the page.
for (int page = 0; page < getPageCount(request, command); page++) {
validatePage(command, errors, page, true);
if (errors.hasErrors()) {
return showPage(request, errors, page);
}
}
}
// No remaining errors -> proceed with finish.
return processFinish(request, response, command, errors);
}
protected void validatePage(Object command, Errors errors, int page, boolean finish) {
validatePage(command, errors, page);
}
protected void validatePage(Object command, Errors errors, int page) {
}
// 子类根据业务逻辑改写
protected void postProcessPage(HttpServletRequest request, Object command, Errors errors, int page)
throws Exception {
}
// 子类根据业务逻辑改写
protected abstract ModelAndView processFinish(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception;
// 子类根据业务逻辑改写
protected ModelAndView processCancel(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception {
throw new ServletException(
"Wizard form controller class [" + getClass().getName() + "] does not support a cancel operation");
}
}
Servlet包装控制器简单的封装了一个内部的Servlet实例。如下代码所示,
public class ServletWrappingController extends AbstractController
implements BeanNameAware, InitializingBean, DisposableBean {
// 包装的Servlet类名,它必须是一个Servlet的完全实现类
private Class servletClass;
// Servlet的名称
private String servletName;
// Servlet的初始化参数
private Properties initParameters = new Properties();
// 可选的Bean名
private String beanName;
// 创建的内部Servlet实例
private Servlet servletInstance;
public void setServletClass(Class servletClass) {
this.servletClass = servletClass;
}
public void setServletName(String servletName) {
this.servletName = servletName;
}
public void setInitParameters(Properties initParameters) {
this.initParameters = initParameters;
}
public void setBeanName(String name) {
this.beanName = name;
}
// 初始化方法继承
public void afterPropertiesSet() throws Exception {
// servlet类必须存在,而且是Servlet的一个实现类
if (this.servletClass == null) {
throw new IllegalArgumentException("servletClass is required");
}
if (!Servlet.class.isAssignableFrom(this.servletClass)) {
throw new IllegalArgumentException("servletClass [" + this.servletClass.getName() +
"] needs to implement interface [javax.servlet.Servlet]");
}
// 如果Servlet名不存在,使用Bean名
if (this.servletName == null) {
this.servletName = this.beanName;
}
// 创建一个Servlet实例
this.servletInstance = (Servlet) this.servletClass.newInstance();
// 模拟调用Servlet的初始化方法
this.servletInstance.init(new DelegatingServletConfig());
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 适配Servlet的服务方法,用于重用已存的Servlet实现的逻辑
this.servletInstance.service(request, response);
return null;
}
public void destroy() {
// 模拟调用析构方法
this.servletInstance.destroy();
}
private class DelegatingServletConfig implements ServletConfig {
public String getServletName() {
// 配置的Sevlet名或者Bean名
return servletName;
}
public ServletContext getServletContext() {
// 过Web应用程序环境传递进来的真正的Sevlet环境
return ServletWrappingController.this.getServletContext();
}
public String getInitParameter(String paramName) {
// 可配置的初始化参数
return initParameters.getProperty(paramName);
}
public Enumeration getInitParameterNames() {
return initParameters.keys();
}
}
}
Servlet转发控制器通过Servlet转发派遣器转发HTTP请求到一个标准的web.xml定义的Servlet组件。如下代码所示,
public class ServletForwardingController extends AbstractController implements BeanNameAware {
// Servlet名称
private String servletName;
// 如果没有制定Servlet名称,则使用Bean名称
private String beanName;
public void setServletName(String servletName) {
this.servletName = servletName;
}
public void setBeanName(String name) {
this.beanName = name;
if (this.servletName == null) {
this.servletName = name;
}
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 取得Servlet的转发派遣器
RequestDispatcher rd = getServletContext().getNamedDispatcher(this.servletName);
if (rd == null) {
throw new ServletException("No servlet with name '" + this.servletName + "' defined in web.xml");
}
// If already included, include again, else forward.
if (useInclude(request, response)) {
rd.include(request, response);
if (logger.isDebugEnabled()) {
logger.debug("Included servlet [" + this.servletName +
"] in ServletForwardingController '" + this.beanName + "'");
}
}
else {
// 使用容器提供的请求派遣功能,转发HTTP请求到Servlet进行处理
rd.forward(request, response);
if (logger.isDebugEnabled()) {
logger.debug("Forwarded to servlet [" + this.servletName +
"] in ServletForwardingController '" + this.beanName + "'");
}
}
return null;
}
protected boolean useInclude(HttpServletRequest request, HttpServletResponse response) {
// 如果它是显示的包含请求或者它已经设置的响应状态代码
return (WebUtils.isIncludeRequest(request) || response.isCommitted());
}
}
URL文件名视图控制器继承自抽象URL视图控制器。抽象URL视图控制器通过URL决定视图名称,并且返回包含此视图的模型和视图对象。然后,具体如何映射URL到视图名,子类需要根据业务逻辑进行实现。如下代码所示,
public abstract class AbstractUrlViewController extends AbstractController {
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
// 取得查找路径,用于日志
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 根据请求URL决定视图名
String viewName = getViewNameForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Returning view name '" + viewName + "' for lookup path [" + lookupPath + "]");
}
return new ModelAndView(viewName);
}
//子类根据业务逻辑实现映射过程
protected abstract String getViewNameForRequest(HttpServletRequest request);
}
public class UrlFilenameViewController extends AbstractUrlViewController {
protected String getViewNameForRequest(HttpServletRequest request) {
// 提取基于请求映射的路径或者查找路径
String uri = extractOperableUrl(request);
return getViewNameForUrlPath(uri);
}
protected String extractOperableUrl(HttpServletRequest request) {
// 首先使用基于请求映射的路径
String urlPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
// 如果没有基于请求映射的路径,则使用查找路径
if (!StringUtils.hasText(urlPath)) {
urlPath = getUrlPathHelper().getLookupPathForRequest(request);
}
return urlPath;
}
protected String getViewNameForUrlPath(String uri) {
String viewName = this.viewNameCache.get(uri);
if (viewName == null) {
// 从URI中提取视图名
viewName = extractViewNameFromUrlPath(uri);
// 添加配置的前缀和后缀
viewName = postProcessViewName(viewName);
this.viewNameCache.put(uri, viewName);
}
return viewName;
}
protected String extractViewNameFromUrlPath(String uri) {
// 去除前缀/好后缀.*
int start = (uri.charAt(0) == '/' ? 1 : 0);
int lastIndex = uri.lastIndexOf(".");
int end = (lastIndex < 0 ? uri.length() : lastIndex);
return uri.substring(start, end);
}
protected String postProcessViewName(String viewName) {
return getPrefix() + viewName + getSuffix();
}
}
可参数化视图控制器的代码非常简单,这里不再进行代码注释。
相关文章推荐
- 深入剖析Spring Web源码(八) - 处理器映射,处理器适配器以及处理器的实现 - 基于简单控制器流程的实现
- 深入剖析Spring Web源码(十四) - 处理器映射,处理器适配器以及处理器的实现 - 处理器的实现架构 - 注解控制器
- 深入剖析Spring Web源码(十一) - 处理器映射,处理器适配器以及处理器的实现 - 处理器映射的实现架构
- 深入剖析Spring Web源码(九) - 处理器映射,处理器适配器以及处理器的实现 - 基于注解控制器流程的实现
- 深入剖析Spring Web源码(十一) - 处理器映射,处理器适配器以及处理器的实现 - 处理器映射的实现架构
- 深入剖析Spring Web源码(十五) - 处理器映射,处理器适配器以及处理器的实现 - 处理器的实现架构 - HTTP请求处理器
- 深入剖析Spring Web源码(十六) - 处理器映射,处理器适配器以及处理器的实现 - 拦截器的实现架构
- 深入剖析Spring Web源码(十六) - 处理器映射,处理器适配器以及处理器的实现 - 拦截器的实现架构
- 深入剖析Spring Web源码(十二) - 处理器映射,处理器适配器以及处理器的实现 - 处理器适配器的实现架构
- 深入剖析Spring Web源码(十五) - 处理器映射,处理器适配器以及处理器的实现 - 处理器的实现架构 - HTTP请求处理器
- 深入剖析Spring Web源码(十) - 处理器映射,处理器适配器以及处理器的实现 - 基于HTTP请求处理器流程的实现
- 深入剖析Spring Web源码(四) - DispatcherServlet的实现
- 深入剖析Spring Web源码(六) - DispatcherServlet的实现 - 派遣器Servlet及其父类
- 深入剖析Spring Web源码(七) - DispatcherServlet的实现 - 根共享环境的加载/其他Servlet
- 深入剖析Spring Web源码(五) - DispatcherServlet的实现 - 通用Servlet和HTTP Servlet
- 深入剖析Spring Web源码(七) - DispatcherServlet的实现 - 根共享环境的加载/其他Servlet
- jQuery.API源码深入剖析以及应用实现(2) - jQuery对象访问和数据缓存
- jQuery.API源码深入剖析以及应用实现(2) - jQuery对象访问和数据缓存
- 深入剖析Spring Web源码(三) - Spring Web MVC工作流
- 深入剖析Spring Web源码(十九) - 整理的文档和日志的索引(第一版)