zoukankan      html  css  js  c++  java
  • Spring MVC简单原理

    Spring MVC原理

    针对有Java Web基础、Spring基础和Spring MVC使用经验者。

    前言

    目前基于Java的web后端,Spring生态应该是比较常见了。虽然现在流行前后端分离,MVC和后端模板渲染越来越少,后端专注向前端提供数据接口。但由于笔者维护着一个老项目,既有JSP技术也有只返回JSON的接口,两者都是基于Spring MVC这一套技术实现的,所以暂且觉得了解一下Spring MVC原理还是有所裨益的。

    Spring MVC工作流

    想必大家第一次学习Spring MVC时都见过这张图

    img

    看完这张图,Spring MVC的工作流基本是一目了然了。建好工程然后web.xml里配个DispatcherServlet(甚至连web.xml都不需要配置,直接通过Java类和@Configuaration进行配置),注解@Controller, @RequestMapping, @Service等等一顿注解搞起来,在IDE中把Tomcat配好,OK,一个Spring MVC的Hello World就跑起来了。问起Spring MVC的原理,我也能按着上图工作流巴拉几句。

    然而,与其说以上是Spring MVC的原理不如说是MVC的模型。Spring MVC怎么就知道把一个请求路由到对应Controller中方法的呢?方法返回了一个ModelAndView对象甚至是一个视图名String,框架带着Model和视图名找到视图(比如最常见的jsp)后怎么搞呢?半路出家的笔者来,可以说是相当好奇了。

    从servlet、web.xml和WEB-INF说起

    Servlet

    Java的Servlet我就不赘述了,直接看类的注释

    A servlet is a small Java program that runs within a Web server.Servlets receive and respond to requests from Web clients, usually across HTTP

    Servlet只是一个规范,必须部署到Servlet容器中才能工作,当然本文说的肯定是HttpServlet,所以常用的容器也就是Tomcat、Jetty等。

    先看Servlet接口,每个方法的注释请参见源码

    public interface Servlet {
    
        public void init(ServletConfig config) throws ServletException;
    
        public ServletConfig getServletConfig();
    
        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
    
        public String getServletInfo();
    
        public void destroy();
    }
    

    其中init方法在Servlet准备对外提供服务时被容器调用,并且整个生命周期中只调用一次,destroy方法当然就是结束时调用了,service方法处理ServletRequest并响应。

    web.xml

    上面说到把Servlet部署到Servlet容器中后,Servlet就可以接受请求了,那容器怎么知道哪个请求对应哪个Servlet?
    OK,我们的web.xml登场了。

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
        <servlet>
            <servlet-name>servlet</servlet-name>
            <servlet-class>com.foo.bar.SomeServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>servlet</servlet-name>
            <url-pattern>/some/url</url-pattern>
        </servlet-mapping>
    </web-app>
    

    如上,最简单的一个web.xml描述文件,描述了什么呢?应该都懂,SomeServlet这个Servlet会处理URL为/some/url的请求,这个SomeServlet大概长这样

    public class SomeServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            PrintWriter writer = resp.getWriter();
            writer.write("hello boys");
            writer.flush();
            writer.close();
        }
    }
    
    

    GET someHost:port/some/url时,就会返回hello boys了。不难想象,请求多的话,得在web.xml中servlet的节点将越来越多,维护起来也是个体力活儿(当然用xml维护的Spring MVC配置文件也没少多少,不过维护起来轻松一些,因为结合了Spring bean,下面再讲)。
    以上应该是对Servlet相关最简单的描述了。

    Spring容器管理bean

    这个用过没用过Spring的应该都有所了解,说一下我自己的理解

    核心就是DI,通过Spring这个IOC容器来管理各个Bean之间的依赖关系,通过Spring在启动时来注入依赖,而不是在写代码时就new出来。

    这里面我觉得比较重要的几个类,也是我认为看Spring代码时比较好串联起来的几个类:

    • BeanDefinition: Spring的定义,最常见的我个人认为最好理解的也就是Xml中定义的bean,此处膜拜一下Spring预留的扩展点,要不然dubbo的bean怎么能通过Spring容器管理呢对吧,此处以后再说。
    • BeanFactory: 好理解,产生Bean的工厂,不多说自己看代码。
    • ApplicationContext: Spring的上下文容器,基本用的就是它,暂时把它当成黑箱,所有的bean从这里getBean获取就行了,其余自行看代码。

    DispatcherServlet

    好,终于到Spring MVC了,先上一个大家都见过的web.xml配置

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
        <servlet>
            <servlet-name>dispatcher</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:applicationContext.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>dispatcher</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </webapp>
    

    注意到几个点:

    • DispatcherServlet这个servlet接管了所有的请求(servlet-mapping的配置)
    • servlet配置里还有个load-on-startup,且值为1
    • init-param这里配置了一个参数contextConfigLocation,其值就是Spring bean的配置文件

    DispatcherServlet接管了所有请求,OK,看到这里大家估计也都明白了,是在这个servlet里的service方法里根据不同url找到不同的controller来处理的;load-on-startup这个参数规定,如果值大于0,会在启动时由容器初始化,并且值越小初始化顺序越靠前;init-param名字已经很直白了,表示这个servlet可以配置的参数,对该servlet可见(对应的还有个context-param,对所有servlet可见,web.xml里还有其他一些可配置的参数,有兴趣可以自行查阅),contextConfigLication的值就是多个定义bean的xml配置文件。

    讲完这些,大家猜应该八九不离十了,总结起来就是一句话:
    Servlet容器启动时调用DispatcherServlet中init方法,同时依托Spring,初始化所有bean,并保存一个url到handler之间的映射
    至于映射关系就好定义了,xml配置、@RequestMapping,都是具体的实现。当然我这是简单粗暴的总结,里面细节性的问题都跳过了不过你自己在init方法里打个断点,debug跟一下,一步一步就摸清全部流程了。

    文章比较短也比较简单,大家也可能都了解,但我觉得这几个点对理清整个流程还是有帮助的。提出的几个问题也没解答,我只能说答案都在代码里,跟一下全明白。写的不好大家也别笑,多给我提提建议,大家相互进步就OK。



    许多年前 你有一双清澈的双眼

    奔跑起来 像是一道春天的闪电

    想看遍这世界 去最遥远的远方

    感觉有双翅膀 能飞越高山和海洋




  • 相关阅读:
    (转)基于REST架构的Web Service设计
    WPF 简易的喷泉效果
    C# 取Visio模型信息的简易方法
    WPF TextBox按字节长度限制输入
    NPOI导出WPF DataGrid控件显示数据
    WPF--TextBlock的ToolTip附加属性
    【转】WPF 从FlowDocument中找到Hyperlink
    WPF 初学VisifireChart
    WPF 简易进度条效果
    WPF 简易的跑马灯效果
  • 原文地址:https://www.cnblogs.com/1024Community/p/8544148.html
Copyright © 2011-2022 走看看