(直接看答案可点击上面 “4 一句话总结:SpringMVC运行原理 ” )
1、什么是 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对Servlet的封装思想,跟上述代码是类似的。
2、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
3、SpringMVC 的 DispatcherServlet
当 SpringMVC 接收到请求时,首先会创建前端控制器DispatcherServlet
这里可以看到,DispatcherServlet 继承自 HttpServlet。而我们知道,Servlet首次加载时,调用顺序是 init() -> service() -> destroy(),我们一个方法一个方法地看。
3.1 init()
(留意中文注释)
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()主要是创建了springmvc容器。
3.2 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 {
...
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());
...
}
}
值得注意的是,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>
3.3 HandlerMapping 实现类
当程序加载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 + "]");
}
}
}
}
3.4 HandlerAdapter 实现类
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);
}
4、一句话总结:SpringMVC运行原理
①. 当在 web.xml 中设置 DispatcherServlet 的 url-pattern 为 / 时,用户发起请求,DispatcherServlet接收。
②. DispatcherServlet 调用 HandlerMapping 解析 URL,并找到对应的控制器方法 (Handler对象)
③. DispatcherServlet 调用 HandlerAdapter,HandlerAdapter 调用 Controller 中的方法
④. 方法执行完成后会返回一个 ModelAndView 对象
⑤. ViewResovler 对 ModelAndView 进行视图解析,返回View
⑥. DispatcherServlet 对 View(如jsp) 进行渲染(将模型数据填充至视图中)
⑦. DispatcherServlet 响应用户
(DispatcherServlet - 前端控制器;Controller - 后端控制器)
5、前端设计模式
DispatcherServlet 的设计使用了前端设计模式。
围绕 (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);
}