Spring-MVC运行原理
一、 Spring-MVC的对象初始化,即 bean放入context的beanFactory中。
1. 对象的初始化工作主要在org.springframework.web.servlet.FrameworkServlet类中的initServletBean方法中完成,initServletBean方法最终会调用到
org.springframework.context.support.AbstractApplicationContext类的refresh方法,refresh方法是主要的bean的初始化方法。refresh方法又调用类里面的obtainFreshBeanFactory方法。
2. org.springframework.beans.factory.support.DefaultListableBeanFactory为默认的BeanFactory,
DefaultListableBeanFactory 类中的registerBeanDefinition方法为保存对象列表信息的主要方法,beanFactory中的对象存放到成员变量Map<String, BeanDefinition> beanDefinitionMap中。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
//---------------------------------------------------------------------
// Implementation of BeanDefinitionRegistry interface
//---------------------------------------------------------------------
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
resetBeanDefinition(beanName);
}
二、 Spring-MVC中Controller中的method与 RequestMappingURL的初始化
1. Controller中的method与 RequestMappingURL的映射关系绑定初始化工作主要在spring-webmvc.jar中完成。这个jar 文件包含Spring MVC 框架相关的所有类,
包括框架的Servlets,Web MVC框架,控制器和视图支持。
2. SpringMVC在容器初始化时,绑定请求URL映射到相应的Controller中的方法的工作主要在org.springframework.web.servlet.handler.AbstractHandlerMethodMapping类中的initHandlerMethods方法完成,
AbstractHandlerMethodMapping实现了Spring的org.springframework.beans.factory.InitializingBean接口,在InitializingBean的afterPropertiesSet即调用了initHandlerMethods。
MappingURL与Controller对应方法的映射关系在servlet容器初始化时保存到 AbstractHandlerMethodMapping中的成员变量urlMap中。
AbstractHandlerMethodMapping类的initHandlerMethods为protected修饰 ,可被子类重写。
三、 请求URL到达映射处理的Controller的Method前的逻辑。
执行业务方法的逻辑主要在org.springframework.web.servlet.DispatcherServlet类的doDispatch方法中。
以下为doDispatch方法的代码:
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1; try {
ModelAndView mv;
boolean errorView = false; try {
processedRequest = checkMultipart(request); // Determine handler for the current request.
mappedHandler = getHandler(processedRequest, false);
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()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
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.
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);
errorView = (mv != null);
} // Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, 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");
}
} // 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);
}
}
}
主要执行的过程有四个步骤,如下所示:
1. 通过调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping类的getHandlerInternal方法来获得相应的请求url的映射的controller和method的HandlerMethod,
AbstractHandlerMethodMapping中的成员变量urlMap保存的即为servlet容器启动时初始化的RequestMapping映射的Controller和Method的信息。
getHandlerInternal方法代码如下:
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>(); List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
} if (matches.isEmpty()) {
// No choice but to go through all mappings
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
} if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator); if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
} Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
m1 + ", " + m2 + "}");
}
} handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}
HandlerMethod类主要包括的信息有 匹配处理的Controller ,处理方法Method,以及方法的传入参数 parameters等信息。HandlerMethod的主要成员变量代码如下:
public class HandlerMethod {
/** Logger that is available to subclasses */
protected final Log logger = LogFactory.getLog(HandlerMethod.class);
private final Object bean;
private final Method method;
private final BeanFactory beanFactory;
private MethodParameter[] parameters;
private final Method bridgedMethod;
}
bean为url映射匹配到Controller, method为映射到的处理方法。
2. 执行url匹配的过滤器的preHandle方法。
3. 执行主要的业务过程处理方法,即执行步骤1中找到的Controller对应的Method。
执行主要的业务处理方法的是在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter类的invokeHandlerMethod方法中调用。
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView} if view resolution is required.
*/
private ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response,
HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory); ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) {
return null;
}
else {
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
}
业务方法调用主要在org.springframework.web.method.support.InvocableHandlerMethod类的invokeForRequest方法中,过程包括组装request的请求参数传入到handleMethod的参数数组args[]中,方法调用等。
方法通过java的反射机制执行,即java.lang.reflect.Method.invoke(Object controller, Object... args); 参数一controller为方法所属的Controller,参数二args为请求参数
/**
* Invoke the method after resolving its argument values in the context of the given request. <p>Argument
* values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs}
* parameter however may supply argument values to be used directly, i.e. without argument resolution.
* Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or
* a thrown exception instance. Provided argument values are checked before argument resolvers.
*
* @param request the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type, not resolved
* @return the raw value returned by the invoked method
* @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception
*/
public final Object invokeForRequest(NativeWebRequest request,
ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) {
StringBuilder builder = new StringBuilder("Invoking [");
builder.append(this.getMethod().getName()).append("] method with arguments ");
builder.append(Arrays.asList(args));
logger.trace(builder.toString());
} Object returnValue = invoke(args); if (logger.isTraceEnabled()) {
logger.trace("Method [" + this.getMethod().getName() + "] returned [" + returnValue + "]");
} return returnValue;
}
4. 执行url匹配的过滤器的postHandle方法。
Spring-MVC运行原理的更多相关文章
- struts1,struts2,hibernate,spring的运行原理结构图
一.struts1运行原理 1.初始化:struts框架的总控制器ActionServlet是一个Servlet,它在web.xml中配置成自动启动的Servlet,在启动时总控制器会读取配置文件(s ...
- (4.1)Spring MVC执行原理和基于Java的配置过程
一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...
- Spring MVC简单原理
Spring MVC原理 针对有Java Web基础.Spring基础和Spring MVC使用经验者. 前言 目前基于Java的web后端,Spring生态应该是比较常见了.虽然现在流行前后端分离, ...
- spring Mvc 执行原理 及 xml注解配置说明 (六)
Spring MVC 执行原理 在 Spring Mvc 访问过程里,每个请求都首先经过 许多的过滤器,经 DispatcherServlet 处理; 一个Spring MVC工程里,可以配置多个的 ...
- Spring MVC执行原理和基于Java的配置过程
一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...
- Spring MVC工作原理(好用版)
Spring MVC工作原理 参考: SpringMVC工作原理 - 平凡希 - 博客园https://www.cnblogs.com/xiaoxi/p/6164383.html SpringMVC的 ...
- Spring Boot运行原理
概述 本文主要写了下Spring Boot运行原理,还有一个小例子. Spring4.x提供了基于条件来配置Bean的能力,而Spring Boot的实现也是基于这一原理的. Spring Boot关 ...
- Spring MVC工作原理 及注解说明
SpringMVC框架介绍 1) spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面. Spring 框架提供了构建 Web 应用程序的全功 ...
- Spring MVC 底层原理
参考博客:http://www.cnblogs.com/xiaoxi/p/6164383.html Spring MVC处理的流程: 具体执行步骤如下: 1 首先用户发送请求给前端控制器,前端控制器根 ...
- Spring Boot 运行原理
Spring Boot并没有任何新的技术,全都是基于Spring4提供的技术,用优秀的设计,为Web开发提供了一套新的方式. 在HelloWorld中,我们没有进行任何显示的配置,但是程序还是运行起来 ...
随机推荐
- Hive实现交叉二维分析的小语句
1. 梳理出你要的列和行维度 列维度: 每一周 行维度: 年级 + 学科 + 班型 2. 对数据按周增序进行聚合 (即根据列维度) ,生成list concat_ws 和 collect_list ( ...
- xcode打包命令
xcodebuild clean -workspace Myproject.xcworkspace -scheme myProject xcodebuild archive -workspace My ...
- Mysql分表:Merge
merge是Mysql最简单的一种分表,Mysql自带的一个分表功能,Merge表并不保存数据,Merge表和分表是对应映射关系.demo: 创建分表:CREATE TABLE `user1` ( ` ...
- Java并发编程随笔
死锁:两个线程互相等待对方释放锁才可以继续运行. 避免死锁的常见方法: 1.避免一个线程同时获取多个锁 2.避免一个线程在锁内同时占用多个资源,尽量保证一个锁只占用一个资源 3.尝试使用定时锁,使用l ...
- HTTP请求头与响应头(转载)
欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/400503 ...
- Golang源码探索(三) GC的实现原理(转)
Golang从1.5开始引入了三色GC, 经过多次改进, 当前的1.9版本的GC停顿时间已经可以做到极短.停顿时间的减少意味着"最大响应时间"的缩短, 这也让go更适合编写网络服务 ...
- logstash 切分tomcat日志
以下配置是logstash切分tomcat catalina.out日志. http://grok.qiexun.net/ 分割时先用这个网站测试下语句对不对,能不能按需切割日志. input { ...
- mkdir npm
mkdir -p 创建目录 没有就创建 有就用 npm init -y 不提示创建package.json 文件
- Linux 标准输入输出、错误输出、重定向标准输出
再来看看 >& 操作符: 重定向操作符 描述 > 将命令输出写入到文件或设备(如打印机),而不是命令提示符窗口或句柄. < 从文件而不是从键盘或句柄读入命令输入. >& ...
- Homework:工作日 还是周末
/* 程序功能: 要求用户从键盘输入1~7之间的整数 如果输入的是1~5, 提示用户是工作日,要努力工作: 如果输入的是6或7,提示用户是休息日,放松休息: 否则,提示用户输入不在合法范围 */ #i ...