一:springmvc运行过程:

1. dispatcherServlet 通过 HandlerMapping 找到controller
2. controller经过后台逻辑处理得到结果集modelandview
3. 视图解析器解析model,渲染view展示页面。

二:springmvc容器是什么:

  很多人喜欢把spring和springmvc混为一谈, 其实它们是完全不同的两个概念。spring主要是创建对像管理依赖的; 而springmvc是一种mvc分层思想将功能模块化,处理用户请求的。而且spring和springmvc是有父子容器关系存在的。子容器可以获取父容器中的对象(可以理解成面向对象中的继承),例如controller通过@Autowired注入spring中的对象, 而如果把controller交给spring来管理,就会出现404异常,因为spring初始化只管理bean,并不会把url和method关联起来。

三:springmvc案例解析:

3.1 自定义springmvc注解@MyController和@MyRequestMapping

3.2 实现过程

public class MyDispatcherServlet extends HttpServlet {
// springmvc 容器对象 key:类名id ,value bean对象
private ConcurrentHashMap<String, Object> springmvcBeans = new ConcurrentHashMap<String, Object>();
// springmvc 容器对象 keya:请求地址 ,vlue 类对象
private ConcurrentHashMap<String, Object> urlBeans = new ConcurrentHashMap<String, Object>();
// springmvc 容器对象 key:请求地址 ,value 方法名称
private ConcurrentHashMap<String, String> handlermapping = new ConcurrentHashMap<String, String>();
/**
* 容器启动时
*/
@Override
public void init() throws ServletException {
// 1. 扫描当前包(及子包)下面的所有类
List<Class<?>> classes = ClassUtil.getClasses("org.wulei.controller");
try {
// 将这些类放到mvc容器
this.findClassMVCAnnotation(classes);
} catch (Exception e) {
}
// 将url和方法绑定起来
this.headlerMapping();
} /* 初始化有controller注解的bean,放入mvc容器。*/
public void findClassMVCAnnotation(List<Class<?>> classes)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
for (Class<?> classInfo : classes) {
// 2. 判断类上是否有加上controller注解
MytController myController = classInfo.getDeclaredAnnotation(MytController.class);
if (myController != null) {
// 默认bean名称是类名小写
String beanName = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
// 3. 实例化对象 , 放入mvc容器
Object object = ClassUtil.newInstance(classInfo);
springmvcBeans.put(beanName, object);
}
}
} /* 通过handlerMapping将url和method关联起来 */
public void headlerMapping() {
// 4. 遍历mvc的bean容器所有的类
for(Map.Entry<String, Object> mvcBean : springmvcBeans.entrySet()) {
Object object = mvcBean.getValue();
Class<? extends Object> classInfo = object.getClass();
// 5. 判断类上面是否有url映射(即:@RequestMapping注解)
MyRequestMapping requestMapping = classInfo.getDeclaredAnnotation(MyRequestMapping.class);
// 6. 获取类上的url地址
String baseUrl = "";
if(requestMapping != null) {
baseUrl = requestMapping.value();
}
// 7. 获取方法上的url映射路径
Method[] methods = classInfo.getDeclaredMethods();
if(methods.length>0) {
for(Method method : methods) {
MyRequestMapping methodRequestMapping = method.getDeclaredAnnotation(MyRequestMapping.class);
if(methodRequestMapping != null) {
// 8. 将 classUrl 和 methodUrl 拼接起来,得到完整url路径。
String methodUrl = baseUrl+methodRequestMapping.value();
// 9. 将url和method绑定起来,存入容器。
urlBeans.put(methodUrl, object);
handlermapping.put(methodUrl, method.getName());
/**
* 到上面容器就就初始化完成,可以对外提供服务了。
* 现在可以每个方法断点调试看运行过程。
*/
}
}
}
}
} /**
* 处理用户请求
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取请求url地址
String url = req.getRequestURI();
if(StringUtils.isEmpty(url)) return;
// 2. 通过url获取容器中的controller对象
Object object = urlBeans.get(url);
if(object == null) {
resp.getWriter().println("ERROR: not found 404 url");
return;
}
// 3. 使用url获取方法.
String methodName = handlermapping.get(url);
if(StringUtils.isEmpty(methodName)) resp.getWriter().println("ERROR: not found method");
// 4. java反射获取被调用的方法
String resultPage = null;
try {
Method method = object.getClass().getMethod(methodName);
resultPage = (String) method.invoke(object);
resp.getWriter().println(resultPage);
} catch (Exception e) {
e.printStackTrace();
}
// 5.调用视图转换器渲染给页面展示
this.myResourceViewResolver(resultPage, req, resp);
}
/**
* 视图解析器
*/
private void myResourceViewResolver(String pageName, HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// 根路径
String prefix = "/";
String suffix = ".jsp";
req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
}

3.3 测试 (正确的url会跳转到相应页面,错误的url会找不到路径)

四:源码解析

4.1 springmvc初始化阶段

4.1.1 我们在用springmvc的时候, 首先会配置 dispatcherServlet,它会拦截符合规则的url请求。

  <servlet>
<servlet-name>WEB</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定springmvc扫描文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring-*.xml</param-value>
</init-param>
<!-- 优先级 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 拦截规则 -->
<servlet-name>WEB</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

4.1.2 可以看出 dispatcherServlet 最终还是继承于httpservlet, 说明它也会满足servlet的工作原理。

  Servlet 生命周期:
    Servlet 加载—>实例化—>服务—>销毁。
    init():在Servlet的生命周期中,仅执行一次init()方法。负责初始化Servlet对象。
    service():负责响应客户的请求。每次请求都会调用相应的doGet/doPost功能。
    destroy():仅执行一次,在服务器端停止且卸载Servlet时执行该方法,负责释放占用的资源。

4.1.3 HttpServletBean 重写了 httpservlet 的 init()方法, 通过initServletBean() 方法来初始化bean, 具体实现在子类FrameworkServlet中。

    /**
* Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization.
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
*/
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
} // 初始化DespatcherService的配置参数
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, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
} // 让子类来初始化bean
initServletBean(); if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}

4.1.4 FrameworkServlet重写了initServletBean(),并在这个方法中调用了initWebApplicationContext()方法

protected WebApplicationContext initWebApplicationContext() {
// 获取 ContextLoaderListener 初始化
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) {
// 将 Spring 的容器设为 SpringMVC 容器的父容器
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
  wac = findWebApplicationContext();
}
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// dispatcherServlet 重写这个方法
onRefresh(wac);
}
if (this.publishContext) {
// 发布这个容器到 ServletContext 中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}

4.1.5 dispatcherServlet 重写onRefresh(),调用initHandlerMappings将url和method关联起来

    /**
* Initialize the HandlerMappings used by this class.
* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
* we default to BeanNameUrlHandlerMapping.
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null; if (this.detectAllHandlerMappings) {
// 获取HandlerMapping实例
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// 排序
OrderComparator.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");
}
}
}

4.2 springmvc运行阶段

4.2.1 上面已经说过每次请求都会执行servlet的service()方法,FrameworkServlet重写了这个方法。

    /**
* Override the parent class implementation in order to intercept PATCH
* requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
     // 获取请求方式例如 get / post
String method = request.getMethod();
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}

4.2.2 然后会根据请求方式去httpsrevlet中调用具体的方法。

    protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException { String method = req.getMethod();
     // get请求
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
     // head请求
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
// post请求
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
// put请求
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp); } else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp); } else {
// 以上都不满足, 说明请求方式有问题。
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

4.2.3 FrameworkServlet重写了这些方法,并最终都是通过 processRequest( ) 方法来处理请求的。

    @Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { processRequest(request, response);
} /**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { processRequest(request, response);
} /**
* Delegate PUT requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { processRequest(request, response);
}
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 获取当前请求的相关信息
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 初始化 ContextHolders
initContextHolders(request, localeContext, requestAttributes); try {
// 最终处理是dispatcherServlet 中的doService()方法调用 doDispatch()来处理的
doService(request, response);
}

4.2.4 doDispatch() 方法的主要过程是通过 HandlerMapping 获取 Handler,再找到用于执行它的 HandlerAdapter,执行 Handler 后得到 ModelAndView

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); // 获取处理请求的具体方法
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
} // 获取处理请求的适配器
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;
} // 得到modelandview
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// 处理请求结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);
}
}
}
}

4.2.5 跳转页面

    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);
}
} // 所有条件都符合就跳转页面
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}

SpringMVC源码解析的更多相关文章

  1. SpringMVC源码解析- HandlerAdapter - ModelFactory(转)

    ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...

  2. springMVC源码解析--ViewResolver视图解析器执行(三)

    之前两篇博客springMVC源码分析--ViewResolver视图解析器(一)和springMVC源码解析--ViewResolverComposite视图解析器集合(二)中我们已经简单介绍了一些 ...

  3. SpringMVC源码解析- HandlerAdapter - ModelFactory

    ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...

  4. springMVC源码解析--ViewResolverComposite视图解析器集合(二)

    上一篇博客springMVC源码分析--ViewResolver视图解析器(一)中我们介绍了一些springMVC提供的很多视图解析器ViewResolver,在开发的一套springMVC系统中是可 ...

  5. springMVC源码解析--HandlerMethodArgumentResolverComposite参数解析器集合(二)

    上一篇博客springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)中我们已经介绍了参数解析相关的东西,并且也提到了HandlerMethodArgume ...

  6. springmvc源码解析MvcNamespaceHandler之<mvc:view-resolvers>

    说在前面 本次主要介绍springmvc配置解析. springmvc配置解析 本次介绍MvcNamespaceHandler. 进入到这个方法org.springframework.web.serv ...

  7. 深入了解SpringMVC源码解析

    Spring MVC源码解析 Spring MVC的使用原理其实是通过配置一个Servlet来接管所有的请求,所有的请求由这个Servlet来进行分发处理. 我们可以从web.xml里面看出这一点 & ...

  8. SpringMVC源码解析- HandlerAdapter初始化

    HandlerAdapter初始化时,主要是进行注解解析器初始化注册;返回值处理类初始化;全局注解@ControllerAdvice内容读取并缓存. 目录: 注解解析器初始化注册:@ModelAttr ...

  9. springmvc源码解析-初始化

    1.      概述 对于Web开发者,MVC模型是大家再熟悉不过的了,SpringMVC中,满足条件的请求进入到负责请求分发的DispatcherServlet,DispatcherServlet根 ...

随机推荐

  1. html article标签 语法

    html article标签 语法 article标签有什么作用?直线电机生产厂家 作用:html中article标签的作用是规定独立的自包含内容,其中外部内容是来自一个外部的新闻提供者的一篇新文章, ...

  2. win7如何设置以管理员身份运行

    一.对所有程序以管理员身份运行 1.右键单击桌面“计算机”,选择“管理” 2.在页面左侧,依此打开“计算机管理(本地)→ 系统工具→本地用户和组→用户”,在右侧找到“Administrator”,双击 ...

  3. iOS 自定义一对UI表现相反的按钮

    假如有一对按钮[重置][提交],要让他们的默认UI和点击的UI表现刚好相反 [提交]按钮,默认橙色,点击边框是橙色,字体是橙色,背景变白色 [重置]按钮,默认白色橙色,边框是橙色,点击字体是白色,背景 ...

  4. 基于RANSAC的点云面分割算法

    该算法在RANSAC和空间检索树的基础上实现的. 算法思路: 1.点云抽希.法线估计 2.出局点索引存储声明 3.平面检测 for (size_t i = 0; i < cloudTemp-&g ...

  5. Mysql 创建函数出现This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA

    This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary mys ...

  6. Leon-ai on WSL

    这几天尝试了下Windows10的WSL,的确是良心产品,虽然还有很多待改进的地方(比如iptabeles之类的功能还未支持). 使用了WSL就避免安装双系统的繁琐,节省很多时间,而且因为是微软自家产 ...

  7. 设计模式(5): vue 不监听绑定的变量

    概述 最近最近做项目的时候总会思考一些大的应用设计模式相关的问题,我把自己的思考记录下来,供以后开发时参考,相信对其他人也有用. 绑定变量 一般情况下,如果我们需要在组件中使用某个变量,会这么使用: ...

  8. C# App.config 自定义 配置节

    1)App.config <?xml version="1.0" encoding="utf-8" ?><configuration>  ...

  9. HAProxy & Keepalived L4-L7 高可用负载均衡解决方案

    目录 文章目录 目录 HAProxy 负载均衡器 应用特性 性能优势 会话保持 健康检查 配置文件 负载均衡策略 ACL 规则 Web 监控平台 Keepalived 虚拟路由器 核心组件 VRRP ...

  10. 【疑难杂症】Firefox 火狐浏览器 关闭到 http://detectportal.firefox.com 的流量

    日期:2019-07-18 00:02:58 作者:Bay0net 介绍: 0x01. 问题描述 火狐浏览器的时候,抓包会出现很多这样的数据包.  具体的 URL http://detectport ...