九零后大叔
技术人的点点滴滴

springmvc篇-(一)servlet的基本使用和核心工作过程

前一章节回顾: 通过前一些章节的了解,我们大致了解了spring的运行的基本机制。但是好像还是和我们常用的场景不太一样,我们常用的场景是打成一个war包,然后丢到tomcat的webapps目录下面,然后tomcat会自动的帮我们启动web应用。那么一个应用请求时如何被我的controller处理的呢?这些问题对于我们仿佛都是一个黑盒,后续的这几个章节我们都会重点分析并弄清楚这个问题。那么在深入了解原理之前,我们首先了解一下servlet是什么,一些基本用法,以及servlet的核心工作原理。

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。在spring没有出现之前,我们开发java服务程序的时候,通常使用的是servlet技术。对于servlet有了解的同学可以略过这一章节,对于不了解servlet的同学可以简单的学一下servlet的基本使用,以及基本的工作原理。那么怎么理解servlet是什么呢?可以理解为spring的controller类,用来处理一类资源的服务请求的servlet处理类。


1.Servlet的基本使用

[1] pom文件添加servlet相关的依赖包

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

[2] web.xml文件的配置

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >

    <web-app>

        <display-name>Archetype Created Web Application</display-name>

        <!-- utf8统一处理 -->
        <filter>
            <filter-name>encodingFilter</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>UTF-8</param-value>
            </init-param>
            <init-param>
                <param-name>forceEncoding</param-name>
                <param-value>true</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>encodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>

        //设置web.xml的url与servlet的映射关系(也就是由哪个servlet处理哪个url请求,类似于spring的controller里面加上@RequestMapping注解,绑定controller和url请求关系)。
        <servlet>
            <servlet-name>minesoft-tutorial</servlet-name>
            <servlet-class>com.minesoft.tutorial.servlet.UserServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>minesoft-tutorial</servlet-name>
            <url-pattern>/user</url-pattern>
        </servlet-mapping>

        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
            <welcome-file>index.htm</welcome-file>
            <welcome-file>index.jsp</welcome-file>
            <welcome-file>default.html</welcome-file>
            <welcome-file>default.htm</welcome-file>
            <welcome-file>default.jsp</welcome-file>
        </welcome-file-list>
    </web-app>

[3] 创建一个如下的UserServlet类

    public class UserServlet extends HttpServlet {

        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

            System.out.println("doPost ---------------");
            System.out.println(req.getMethod());
            System.out.println(req.getRequestURI());
            System.out.println(req.getServletPath());
            System.out.println(req.getServerPort());
        }

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

            System.out.println("doGet ---------------");
            System.out.println(req.getMethod());
            System.out.println(req.getRequestURI());
            System.out.println(req.getServletPath());
            System.out.println(req.getServerPort());

            resp.setContentType("text/json;charset=UTF-8");
            resp.setCharacterEncoding("UTF-8");
            PrintWriter out = resp.getWriter();

            User user = new User();
            user.setId(1000L);
            user.setName("张山");
            user.setIdentity("43057818787873628X");
            user.setBankcard("36457736355363");
            user.setMobile("16752652625");
            user.setGender(2);
            user.setAge(18);
            String str = JSONObject.toJSONString(user);
            out.println(str);
            out.flush();
            out.close();
        }
    }

完成上述的配置之后,我们将工程运行起来,然后前往浏览器地址栏输入http://localhost:8081/user,就能看到doGet中设置的json返回对象了。从上述过程,我们基本上了解了servlet的使用过程,同样的业务功能,只不过相对于spring而言开发更加复杂而已(同样也说明了spring的价值所在,大大的简化了我们的业务开发过程)。

2.Servlet的核心工作过程(也就是HttpServlet模板类的模板方法)


我们知道,首先一个http请求经过浏览器的封装,然后经过硬件线路最终转发到服务器的tomcat容器。其次tomcat接收到这个请求之后,根据web.xml的相应的配置(也就是配置servlet-mapping,参见上面部分的servlet-mapping配置),最终转发到我们的业务Servlet类。再次这个Servlet类对应的doGet方法,doPost方法进行相应的业务处理。至于浏览器是如何封装一个http请求的,硬件线路是如何传输这个请求的,还有tomcat是如何转发给servlet的,这章节我们暂时不去做讨论,留到后续的tomcat的里面进行分析。我们只要知道经过前面一系列的处理,tomcat最终会把一个http请求委托给业务Servlet类的service方法即可。那么从service开始,是怎么一步一步的将这个http请求转给doGet,doPost进行处理的呢?下面我们将进行细致的分析:

[1] tomcat调用servlet的service方法,处理一个http请求。

    @Override
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException
    {
        HttpServletRequest  request;
        HttpServletResponse response;

        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }

[2] service方法又将http请求委托给另外一个service方法处理,这个方法将http请求进行分类处理。类型包括GET,POST,PUT,DELTE,OPTIONS,TRACE等等。

    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        String method = req.getMethod();

        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 = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            //如果是POST方法
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            //如果是PUT方法
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
            //如果是DELETE方法
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            //如果是OPTIONS方法
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            //如果是TRACE方法
            doTrace(req,resp);

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            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);
        }
    }

[3] 如果是GET方法,那么该请求会被交给doGet方法进行处理

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_get_not_supported");
    if (protocol.endsWith("1.1")) {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
}

而因为我们的UserServlet业务Servlet重写了doGet方法,所以实际运行的时候不会调用上面父类的doGet方法,而是调用子类的doGet方法。

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    System.out.println("doGet ---------------");
    System.out.println(req.getMethod());
    System.out.println(req.getRequestURI());
    System.out.println(req.getServletPath());
    System.out.println(req.getServerPort());

    resp.setContentType("text/json;charset=UTF-8");
    resp.setCharacterEncoding("UTF-8");
    PrintWriter out = resp.getWriter();

    User user = new User();
    user.setId(1000L);
    user.setName("张山");
    user.setIdentity("43057818787873628X");
    user.setBankcard("36457736355363");
    user.setMobile("16752652625");
    user.setGender(2);
    user.setAge(18);
    String str = JSONObject.toJSONString(user);
    out.println(str);
    out.flush();
    out.close();
}

通过上述的过程,我们基本上知道了一个http请求是如何被tomcat接受到后,最终被交给用户的具体的业务处理方法处理的了。

更多知识请关注公众号

赞(0) 赏一下
未经允许不得转载:九零后大叔的技术博客 » springmvc篇-(一)servlet的基本使用和核心工作过程
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

九零后大叔的技术博客

联系我们联系我们

感谢您的支持

支付宝扫一扫打赏

微信扫一扫打赏