(直接看答案可点击上面 “一句话总结:SpringMVC运行原理” )
什么是SpringMVC?
SpringMVC是对Servlet的封装
SpringMVC是对Servlet的封装
SpringMVC是对Servlet的封装
在没有使用SpringMVC框架的时候,如果想写一个Servlet请求分发器,我们会怎么写呢?
//接收除jsp外的所有请求,再进行分发
@WebServlet("/")
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String result = req.getParameter("control");
if (result.equals("m1")) {
method1();
} else if (result.equals("m2")) {
method2(req, resp);
} else if (result.equals("m3")) {
method3();
}
}
private void method1() {
}
private void method2(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("demo.jsp").forward(req, resp);
}
private void method3() {
}
}
以上是我们自定义的DispatcherServlet,但是SpringMVC有它自己的想法,SpringMVC对Servlet的封装思想,跟上述代码是类似的。
SpringMVC的重要组件
为了完成请求分发和处理,SpringMVC设计了以下组件
//前端控制器(分发器);相当于@WebServlet("/")
DispatcherServlet ds
//映射器,解析请求格式,判断要执行哪个方法
//相当于 if(req.getParameter("control").equals("m1"))
HandlerMapping hm
//适配器,负责调用具体的方法;相当于method1();
HandlerAdapter ha
//视图解析器,解析结果,准备跳转到具体的物理视图
//相当于req.getRequestDispatcher("demo.jsp").forward(req, resp);
ViewResolver vr
SpringMVC 的 DispatcherServlet
当SpringMVC接收到请求时,首先会创建前端控制器DispatcherServlet
这里可以看到,DispatcherServlet 继承自 HttpServlet。而我们知道,Servlet首次加载时,调用顺序是 init() -> service() -> destroy(),我们一个方法一个方法地看。
- init()
HttpServletBean.class中(留意中文注释即可)
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// Let subclasses do whatever initialization they like.
//进入
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
FrameworkServlet.class中
protected final void initServletBean() throws ServletException, BeansException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//进入
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (BeansException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
protected WebApplicationContext initWebApplicationContext() throws BeansException {
//SpringMVC容器(ConfigurableWebApplicationContext)
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
// No fixed context defined for this servlet - create a local one.
//Spring容器
WebApplicationContext parent =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//spring容器是springmvc容器的父容器
wac = createWebApplicationContext(parent);
}
if (!this.refreshEventReceived) {
// Apparently not a ConfigurableApplicationContext with refresh support:
// triggering initial onRefresh manually here.
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
至此,init方法结束,可以看到init()主要是创建了springmvc容器。
- 下面我们来看service()
DispatcherServlet.class中
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String requestUri = new UrlPathHelper().getRequestUri(request);
logger.debug(
"DispatcherServlet with name '" + getServletName() + "' received request for [" + requestUri + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
//把请求参数做成一个HashMap保存
Map attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
try {
//进入
doDispatch(request, response);
}
finally {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
// Expose current LocaleResolver and request as LocaleContext.
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);
// Expose current RequestAttributes to current thread.
RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
if (logger.isDebugEnabled()) {
logger.debug("Bound request context to thread: " + request);
}
try {
ModelAndView mv = null;
try {
processedRequest = checkMultipart(request);
// Determine handler for the current request.
//mappedHandler相当于是HandlerMapping映射器;解析并匹配 handler
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Apply preHandle methods of registered interceptors.
//调用拦截器方法(与本主题无关)
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// Actually invoke the handler.
// 适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//HandlerAdapter调用方法;生成ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// Apply postHandle methods of registered interceptors.
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" +
getServletName() + "': assuming HandlerAdapter completed request handling");
}
}
// Trigger after-completion for successful outcome.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
finally {
// Clean up any resources used by a multipart request.
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
// Reset thread-bound context.
RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
// Clear request attributes.
requestAttributes.requestCompleted();
if (logger.isDebugEnabled()) {
logger.debug("Cleared thread-bound request context: " + request);
}
}
}
值得注意的是,HandlerMapping和HandlerAdapter都是在DispatcherServlet类的方法中被使用的。我们再来看看HandlerMapping和HandlerAdapter内部都做了什么?
选取HandlerMapping的实现类SimpleUrlHandlerMapping和HandlerAdapter的实现类SimpleControllerHandlerAdapter进行考察。
首先在springmvc.xml 配置文件中
<!-- 控制器 -->
<bean id="demo123" class="com.bjsxt.controller.DemoController"></bean>
<bean
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<!-- 解析出来的控制器逻辑名 -->
<entry key="demo" value-ref="demo123"></entry>
</map>
</property>
</bean>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
当程序加载springmvc.xml时,SimpleUrlHandlerMapping.class中
protected void registerHandlers(Map urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
Iterator it = urlMap.keySet().iterator();
while (it.hasNext()) {
String url = (String) it.next();
Object handler = urlMap.get(url);
// Prepend with slash if not already present.
//这里代表上述key="demo"中无论是否加 /,最终都有/
if (!url.startsWith("/")) {
url = "/" + url;
}
// Remove whitespace from handler bean name.
if (handler instanceof String) {
handler = ((String) handler).trim();
}
//url对应“/demo”;handler对应“demo123”
//进入
registerHandler(url, handler);
}
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
//这里拿到了id="demo123"的bean,即对应的控制器
resolvedHandler = getApplicationContext().getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map handler [" + handler + "] to URL path [" + urlPath +
"]: There is already handler [" + resolvedHandler + "] mapped.");
}
}
else {
if (urlPath.equals("/")) {
if (logger.isDebugEnabled()) {
logger.debug("Root mapping to handler [" + resolvedHandler + "]");
}
setRootHandler(resolvedHandler);
}
else if (urlPath.equals("/*")) {
if (logger.isDebugEnabled()) {
logger.debug("Default mapping to handler [" + resolvedHandler + "]");
}
setDefaultHandler(resolvedHandler);
}
else {
// 做一个(解析后的)url路径和对应控制器方法的映射表
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isDebugEnabled()) {
logger.debug("Mapped URL path [" + urlPath + "] onto handler [" + resolvedHandler + "]");
}
}
}
}
再来看SimpleControllerHandlerAdapter.class
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 调用控制器方法
// 本例中是调用(id="demo123"的)com.bjsxt.controller.DemoController中的方法
return ((Controller) handler).handleRequest(request, response);
}
一句话总结:SpringMVC运行原理
①.当在web.xml中设置DispatcherServlet的为 / ,用户发起请求,DispatcherServlet接收。
②.DispatcherServlet调用HandlerMapping解析URL,并找到对应的控制器方法(Handler对象)
③. DispatcherServlet调用HandlerAdapter,HandlerAdapter调用Controller中的方法
④.方法执行完成后会返回一个ModelAndView对象
⑤. ViewResovler对ModelAndView进行视图解析,返回View
⑥. DispatcherServlet对View(如jsp)进行渲染(即将模型数据填充至视图中)
⑦. DispatcherServlet响应用户
(DispatcherServlet - 前端控制器;Controller - 后端控制器)
ps:前端设计模式
围绕 (web.xml中配置的) DispatcherServlet进行初始化,即DispatcherServlet做中央处理器。
- DispatcherServlet中initStrategies方法
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
}