- Model View Controller 模型-视图-控制器
SpringMVC的总体设计
当我们想要使用Spring MVC时,总会在web.xml中配置一个DispatcherServlet,如下
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
在Servlet的init方法调用时DispatcherServlet执行Spring MVC的初始化工作
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
- initMultipartResolver:初始化MultipartResolver,用于处理文件上传服务,如果有文件上传,那么会将当前的HttpServletRequest包装成DefaultMultipartHttpServletRequest,并且将每个上传的内容封装成CommonsMultipartFile对象。
- initLocaleResolver:用于处理应用的国际化问题,通过解析请求的Locale和设置响应的Locale来控制应用中的字节编码问题。
- initThemeResolver:用于定义一个主题。
- initHandlerMappings:用于定义用户的请求映射关系。HandlerMapping必须定义,如果没有定义,将获取DispatcherServlet.properties文件中默认的两个HandlerMapping,分别是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping。
- initHandlerAdapters:用于根据Handle的类型定义不同的处理规则。同样对HandlerAdapter也必须定义,如果没有定义,将获取DispatcherServlet.properties文件中默认的4个HandlerAdapter,分别是HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、AnnotationMethodHandlerAdapter(注解相关)和ThrowawayControllerHandleAdapter(后面的版本中没有这一个)
initHandlerExceptionResolvers:当Handler处理出错时,会通过这个Handler来统一处理,默认的实现类是SimpleMappingExceptionResolver,将错误日志记录在log文件中,并且转到默认的错误页面。
initRequestToViewNameTranslator:将指定的ViewName按照定义的RequestToViewNameTranslator替换成想要的格式,如加上前缀和后缀等。
- initViewResolvers:用于将View解析成页面。默认的解析策略是InternalResourceViewResolver,按照JSP页面来解析。
来看部分源码:
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
detectAllHandlerMappings是DispatcherServlet的一个属性,你是可以在web.xml中配置的,默认是true,如果为true,则会去从本工程mvc-servlet.xml文件中去探测所有实现了HandlerMapping的bean,如果有,则加入DispatcherServlet的handlerMappings中。如果detectAllHandlerMappings为false,则直接去容器中找id=”handlerMapping”且实现了HandlerMapping的bean.如果以上都没找到,则会去加载默认的HandlerMapping。
来看一下默认的配置都有哪些:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
#默认的HandlerMapping配置
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
#默认的HandlerAdapter配置
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
当你没有在 mvc-dispatcher-servlet.xml中显示配置时,会加载以上默认配置
SpringMVC的初始化
从上面的初始化策略可以看出,在一个请求中可能需要我们来扩展的地方都定义了扩展点。只要试下相应的接口类,并创建一个Spring Bean就能扩展Spring MVC框架。
在Spring MVC框架中,有3个组件是用户必须要定义和扩展的:定义URL映射规则、实现业务逻辑的Handle实例对象,渲染模板资源。 而连接Handler对象和模板渲染的纽带就是Model模型。
Spring容器的创建是在FrameworkServlet的initServletBean()方法中完成的,这个方法会创建WebApplicationContext对象,并调用其refresh()方法来完成配置文件的加载,配置文件的加载同样是先查找Servlet的init-param参数中设置的路径,如果没有,会根据namespace+Servlet的名称来查找XML文件。Spring容器在加载时会调用DispatcherServlet的initStrategies方法来完成在DispatcherServlet中定义的初始化工作。在initStrategies方法中会初始化Spring MVC框架需要的8个组件,这8个组件对应的8个Bean对象都保存在DispatcherServlet类中。
Control设计
Spring MVC的Control主要由HandlerMapping和HandlerAdapters两个组件来提供。HandlerMapping负责映射用户的URL和对应的处理类,HandlerMapping并没有规定这个URL与应用的处理类如何映射,在HandlerMapping接口中只定义了根据一个URL必须返回一个有HandlerExecutionChain代表的处理链,我们可以在这个处理链中添加任意的HandlerAdapters实例来处理这个URL对应的请求。
以上设计思路很像Servlet中的Filter处理。
HandlerMapping初始化
举例:SimpleUrlHandlerMapping类是如何初始化的?
- 首先调用ApplicationObjectSupport的setApplicationContext()方法。
- 调用SimpleUrlHandlerMapping的initApplicationContext()方法
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
它调用的是父类的同名方法
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
初始化initInterceptors()方法:将SimpleUrlHandlerMapping中定义的interceptors包装成handlerInterceptor对象保存在adaptedInterceptors数组中。
- SimpleUrlHandlerMapping继续执行registerHandlers(this.urlMap)方法
在方法registerHandlers中,调用了AbstractUrlHandlerMapping的registerHandler(url, handler)方法;
在方法registerHandler(url, handler)中,将在SimpleUrlHandlerMapping中定义的mappings注册到handlerMap集合中。
※HandlerMapping初始化工作完成的两个最重要的工作就是:将URL与Handler的对应关系保存在HandlerMapping集合中,并将所有的interceptors对象保存在adaptedInterceptors数组中,等请求到来的时候执行所有的adaptedIntercoptors数组中的interceptor对象。
interceptor的主要作用是拦截用户的请求并进行相应的处理。
HandlerAdapter初始化
HandlerAdapter可以帮助自定义各种handler。
这个Handler必定要符合某种规则,最常见的方法就是我们的所有handler都继承某个接口,然后SpringMVC 自然就调用这个接口中定义的特性方法。
handlerAdapter的初始化没有什么特别之处,只是简单的创建一个handlerAdapter对象,将这个对象保存在DispatcherServlet的HandlerAdapters集合中。当Spring MVC将某个URL对应到某个Handler时候,在handlerAdapters集合中查询哪个handlerAdapter对象supports这个Handler,handlerAdapter对象将会被返回,用了调用这个handlerAdapter接口对应的方法。
Control的调用逻辑
整个SpringMVC 的调用是从DispatcherServlet 的doService 方法开始的,在doService方法中会将ApplicationContext 、localeResolver 、tbemeResolver 等对象添加到request 中以便于在后面使用。
接着就是调用doDispatch 方法,这个方法是主要的处理用户请求的地方。
Control 的处理逻辑关键就是在DispatcherServlet 的handlerMappings 集合中根据请求的URL 匹配每个HandlerMapping 对象中的某个Handler , 匹配成功后将会返回这个Handler的处理链HandlerExecutionChain 对象,而在这个HandlerExecutionCbain 对象中将会包含用户自定义的多个Handlerlnterceptor 对象。
在Handlerlnterceptor 接口中定义的3个方法中, preHandle 和postHandle 分别在Handler 执行前和执行后执行,afterCompletion 在View渲染完成、DispatcherServlet 返回之前执行。这里需要注意的地方是,当preHandle 返回false时, 后续操作均不执行,直接return。
看看HandlerExecutionChain 类的getHandler 方法你会发现返回的是Object 对象,所以在这里Handler 对象是没有类型的,Handler 的类型是由HandlerAdapter 决定的。
DispatcherServlet 会根据Handler 对象在其handlerAdapters 集合中匹配哪个HandlerAdapter实例来支持该Handler 对象, 接下来执行Handler对象的相应方法,如该Handler 对象的相应方法返回一个ModeLAndView 对象,接下来就去执行View渲染。
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 {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
几个重要的步骤:
1、mappedHandler = getHandler(processedRequest);
得到一个HandlerExecutionChain,调用如下方法
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;
}
}
// handlerMappings遍历结束还是没有返回值,则返回null
return null;
}
HandlerExecutionChain handler = hm.getHandler(request);
调用AbstractHandlerMapping类中的方法,生成一个HandlerExecutionChain并返回
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request); // 生成一个handler
if (handler == null) {
handler = getDefaultHandler(); //这个DefaultHandler要自己设置(?)默认是null
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
getHandlerInternal(request)是个抽象方法,由具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,如果handler为String类型,则表示这个则会去Spring容器里面去找这样名字的bean。
2、HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
具体实现
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,判断他们是否支持这个handler。
我们来看下HttpRequestHandlerAdapter的supports(handler)方法:
SimpleControllerHandlerAdapter也是默认的4个adapters之一,针对实现了Controller接口的实现类
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
//就是判断handler是否实现了Controller接口
return (handler instanceof Controller);
}
@Override
// handle方法
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//会调用实现Controller接口时重写了的该方法
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;
}
}
Model
ModleAndView对象是连接业务逻辑层与View展现层的桥梁,对于Spring MVC来说就是连接Handler和View的桥梁。ModelAndView 会持有一个ModelMap对象和一个View对象或者View的名称。ModelMap其实就是个Map。
public class ModelMap extends LinkedHashMap<String, Object>
一个例子
@RequestMapping(value="admin/blogs",method= RequestMethod.GET)
public String getBlogs(ModelMap modelMap){
List<BlogEntity> blogList = blogRepository.findAll();
modelMap.addAttribute("blogs",blogList); //这里换成blogs,则jsp文件中判断条件也要更改为blogs
return "blogs"; // jsp文件名
}