demo-springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring/springmvc.xml
1
demo-springmvc
/
Servlet 创建时可以直接调用无参数的 init 方法。HTTPServletBean的 init方法如下:
public final void init() throws ServletException {
try {
// 将Servlet 中配置的参数封装到
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
initServletBean();
}
BeanWrapper的作用参考:
从HttpServletBean 中可知,FrameworkServlet的初始化入口方法应该是initServletBean;
protected final void initServletBean() throws ServletException {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
上面只有两句核心代码:
下面看一下initWebApplicationContext方法:
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
onRefresh(wac);
}
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
initWebApplicationContext 方法做了三件事情:
获取跟容器的原理是,默认情况下spring会将自己的容器设置成ServeletContext的属相,默认的跟容器的key为:org.springframework.web.context.WebApplicationContext.ROOT,定义下
org.springframework.web.context.WebApplicationContext 中。
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
所以获取根容器只需要调用ServletContext 的getAttribute 就可以了。
ServletContext#getAttribute("org.springframework.web.context.WebApplicationContext.ROOT")
其中createWebApplicationContext 的初始化过程:
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
其中initStrategies (初始化mvc环境)
DispatcherServlet 核心类如下:
DispatcherServlet 请求过程:
1、 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
2、 DispatcherServlet——> 遍历注册的HandlerMappings,然后根据request 获取执行链对象(HandlerExecutionChain),包含一个Handler处理器,多个HandlerInterceptor拦截器对象。
3、 DispatcherServlet——>遍历系统中注册的HandlerAdapters 与 handler获取一个handler适配器。
4、调用执行链,调用拦截器当中的preHandle() 方法,进行预处理。
4、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
5、通过执行链,去调用拦截器当中的postHandle()方法,进行拦截处理。
5、 ModelAndView的逻辑视图名——> 遍历注册的viewResolves 与 返回的viewName进行匹配
6、 View——>渲染,调用view.render() 进行视图解析和返回,设置model到request中;
7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
8、异常-遍历handlerExceptionResolvers 调用.......,最后跳转至异常MV
DispatcherServlet 核心代码如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//1.检查是不是上传的请求,如果是将通过MultipartResolver解析
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 2.请求到处理器的映射,通过HandlerMapping 进行映射
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3.遍历注册的HandlerAdapters 找到Handler相对应的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理GET、HEAD请求的Last-Modified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet)
{
return;
}
}
// 执行相应Interceptor 的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 由适配器执行对象的处理器(调用处理器相应功能处理方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//如果需要异步处理,直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//当view 为空时(比如,Handler返回值为void),根据request设置默认view
applyDefaultViewName(processedRequest, mv);
// 执行相应的Interceptor 的postHandle方法,此方法的执行实在返回ModeAndView之前
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
// 处理返回结果。包括异常防护力、渲染页面、发出完成 通知触发Inceptor的afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
// 解析视图进行视图的渲染,由ViewResolver解析view,视图在渲染时会把Model传入
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
HandlerMapping实现类结构:
:基于ioc name中以 /开头的Bean时 注册到映射
: 基于手动配置url 与 Controller映射
: 基于注解方法配置对应映射
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
方法的实现非常灵活,只要使用Request返回HandlerExecutionChain就可以了,下面是实现一个自定义的HandlerMapping:
public class DemoHandlerMapping implements HandlerMapping {
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
String url = request.getRequestURI().toString();
String method = request.getMethod();
if (url.startsWith("/demo1")) {
return "demo1对应的handle";
}
return null;
}
}
查找Handler是按顺序遍历所有的Handlermapping,当找到一个Handlermapping后立即终止并返回,代码如下:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
HandlerAdapter 实现类结构图:
HandlerAdapter 可以理解为使用处理器干活的人。它里面一共有3个方法:
下面介绍一个SpringMVC 自己的HandlerAdapter-SimpleHandlerAdapter,代码如下:
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
上面是用来使用实现了Controlelr接口的处理器干活的,干活的方法是直接调用处理器的 handlerRequest 方法。
选择使用哪个HandlerAdapter 的过程在getHandlerAdapter方法中,它的逻辑时遍历所有的Adapter,然后检查哪个可以处理当前的Handler, 找到第一个可以处理的Handler的Adapte后就停止查找并将其返回。getHandlerAdapter方法如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
HandlerAdapter 主要注册到Spring MVC的容器里,注册方法和HandlerMapping一样,只要配置一个Bean就可以了。
Handler是从HandlerMapping里返回的。
ViewResolver 实现类图
ViewResolver 用来将String 类型的视图名和Locale解析为 View 类型的视图,ViewResolver 接口如下:
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
从接口方法的定义可以看出解析视图所需要的参数是手透明和Locale, 不过一般情况下我们只需要根据视图名找到对应的视图,然后渲染就行,并不需要对不同的区域使用不同的视图进行显示,
View 是用来渲染页面的,通俗来讲就是要将程序返回的参数填入模板中,生成html(也可能是其他类型)文件。这里有两个关键问题:
这就是viewResolver 主要要做的工作,ViewSolver需要找到渲染所用的模板和所用的技术(也是视图的类型)进行渲染,
具体的渲染过程则交给不同的视图自己完成。
我们最常使用的UrlBaseViewResolver 系列的解析器都是针对单一视图类型进行解析的,只需要找到使用的模板就可以了。
比如,InternalResourceViewResolver只针对jsp类型的视图,FreeMarkerViewResolver只针对FreeMarker,VelocityViewResolver只针对Velocity。而ResourceBundlerViewResolver、XmlViewResolver、BeanNameViewResolver等解析器可以同时解析多种类型的视图。
ResourceBundleViewResolver 配置如下:
1. ViewResolve的作用就是通过解析MdoelAndView,将MdoelAndView中的逻辑视图名变为一个真正的View对象,并将MdoelAndView中的Model取出。
2.View的作用就是在获取到ViewResolve传来的View和Model,对Model进行渲染,通过View对象找到要展示给用户的物理视图,将渲染后的视图展示给用户。用很直白的话将就是将数据通过request存储起来,找到要展示给用户的页面,将这些数据放在页面中,并将页面呈现给用户。
ViewResolver 源码介绍
ViewResolve和前面的HandlerMapping,HandlerAdapter一样,首先是在启动服务的时候,IOC容器会根据配置文件里面的ViewResolve相关信息对ViewResolve进行实例化,并存储到DispatcherServlet的 List
下面是DispatcherServlet类里面取出IOC容器里面的viewResolver相关对象,可以看成是DispatcherServlet对viewResolver的注册。
ViewResolver 的初始化过程:
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
if (this.detectAllViewResolvers) {
// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
Map matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList(matchingBeans.values());
// We keep ViewResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
else {
try {
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default ViewResolver later.
}
}
if (this.viewResolvers == null) {
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
}
}
}
再看看
viewResolver拿到了ModelAndView对象后是怎么处理的
DispatcherServlet的doDispatch方法中有一个render(mv, processedRequest, response);render就是对ModelAndView对象进行解析
DispatcherServlet的render方法
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// 通过视图名获取到真正的View对象.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
view = mv.getView();
}
//视图渲染
view.render(mv.getModelInternal(), request, response);
}
protected View resolveViewName(String viewName, Map model, Locale locale,
HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
我们再看ViewResoler的resolveViewName方法。InternalResourceViewResolver的祖先类AbstractCachingViewResolver中有resolveViewName方法。
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
synchronized (this.viewCache) {
View view = this.viewCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
return view;
}
}
}
AbstractCachingViewResolver中的resolveViewName方法,该方法首先会判断有没有缓存,要是有缓存,它会先去缓存中通过viewName查找是否有View对象的存在,要是没有,它会通过viewName创建一个新的View对象,并将View对象存入缓存中,这样再次遇到同样的视图名的时候就可以直接在缓存中取出View对象了
AbstractCachingViewResolver中的createView方法
protected View createView(String viewName, Locale locale) throws Exception {
return loadView(viewName, locale);
}
AbstractCachingViewResolver中的loadView方法是一个抽象方法,它通过AbstractCachingViewResolver的子类UrlBasedViewResolver方法实现
UrlBasedViewResolver中的loadView方法
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
View result = (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
return (view.checkResource(locale) ? result : null);
}
我觉得buildView方法是创建View对象最核心的方法,buildView方法会获取一个View对象,这个对象会将视图以什么格式呈现给用户,例如如果是jsp显示呈现给用户的话,那这个view对象就是JstlView,默认的是JstlView。在这个方法中我们看到了getPrefix() + viewName + getSuffix()这样一段代码,这就是对视图路径的一个拼接了,getPrefix()方法获取前缀,也就是我们在配置文件中配置的
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
return view;
}
在SpringMVC 中有一个专门的角色 对异常情况进行处理,在SpringMVC 中就是HandlerExceptionResolver。具体来说,此组件的作用就是根据异常设置ModelAndView,之后再交给render方法进行渲染。render只负责 将ModelAndView渲染成页面,
通过doDispatcher的分析可以知道HandlerExceptionResolver只是用于解析对请求做处理的过程中产生的异常,而渲染环节产生的异常不归它管。它是在render之前工作的,先解析出ModeAndView之后render才去渲染,当然它就不能处理render过程中的异常了。HandlerExceptionResolver接口定义如下:
public interface HandlerExceptionResolver {
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}