• servlet总结


    servlet总结

    servlet总结

    1 Servlet的概念

    要理解Servlet,首先得从最早的网页开始讲起。最早时,网页是静态的,我们通过url定位到一个静态的html网页,然后,服务器把这个网页传输回去,这样就完成了一次访问。但是,这时的网页是静态的,甚至是单文件的。后来,为了把相同类型的元素之间的属性归类,只需要定义一次,所以,这种类型的元素可以共享属性,于是就发明了css,把元素的属性归类集中定义。但是,html语言是固定的。它本身没有任何控制语句,没有if判断,也没有循环。不能动态的生成网页。比如,根据输入的行数改变表格的行数等,这个只通过html是无法实现的,它必须借助于另外一门有控制语句的语言。这些语言有很多,比如JavaScript、Python、Tcl等等。通过这样语言动态的输出html的元素,可以动态的生成网页。我们说到的这些都还是前端的,就是我们访问一个网页的时候,把html、css、还有js以及一些多媒体文件一起返回。其实从服务器的角度来说还是静态的,虽然,我们的网页确实是动态生成的了,但这是在客户端,通过浏览器执行js等前端语言动态生成的html。
    那么,从服务器的角度来看,怎样才算是动态的呢?那就是,当我们在url中使用不同的参数提交给服务器时,服务器可以根据参数,动态的生成html。那才能算是从服务器端实现了动态网页。
    那这个是如何实现的呢?最早的实现叫CGI(common gateway interface),这些CGI的实现语言有很多种,早期Sun公司Tcl做CGI很流行,后来,Sun公司收购了Java。逐渐改为使用Java作为CGI的实现语言。
    而Servlet是使用Java实现的一个CGI程序的封装。它把一些常见的处理流程封装起来,使用者只需继承它,修改几个重要的处理函数就可以了。所以,Servlet就是一个Java版的后台处理程序,它能理解客户发送过来的请求。并能根据请求进行处理,然后,返回处理结果。而我们平时使用的最多的,应该是Servlet基于http的协议的一个实现,叫HttpServlet。就是说客户发送http协议的请求,HttpServlet能够理解,并响应请求。

    2 HttpServlet的使用

    Java现在流行的构建工具是gradle,所以,我们这里也使用gradle来进行展示。关于各种构建工具的比较参见:
    http://www.cnblogs.com/yangwen0228/p/6534655.html

    2.1 程序结构如下:

    Directory tree
    ==============
    [-] src
     `--[-] main
         |--[-] java
         |   `--[-] cn
         |       `--[-] shinysw
         |           `--[-] servlet
         |               `----- Servlet1.java
         `--[-] webapp
             `--[-] WEB-INF
                 `----- web.xml
    

    2.2 build.gradle配置文件如下:

    apply plugin: 'war'
    apply from: 'https://raw.github.com/akhikhl/gretty/master/pluginScripts/gretty.plugin'
    
    dependencies {
      compile 'javax.servlet:javax.servlet-api:3.1'
    }
    

    2.3 Servlet1.java文件如下:

    package cn.shinysw.servlet;
    
    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    public class Servlet1 extends HttpServlet {
        private String message;
    
        public void init() throws ServletException {
            message = "Hello World";
        }
    
        public void doGet(HttpServletRequest request,
                          HttpServletResponse response)
            throws ServletException, IOException {
            response.setContentType("text/html");
    
            PrintWriter out = response.getWriter();
            out.println("<h1>" + message + "</h1>");
        }
    
        public void destroy() {
            // do nothing.
        }
    }
    

    我们这里的Servlet1就是继承于HttpServlet的,只要有这个HttpServlet在,那么任何向它的请求就能由它的定义函数来进行处理了。它的几个主要处理函数分别是:

    • init()
      用于初始化变量或者状态。
    • doGet()
      用于处理http中的get请求。
    • doPost()
      用于处理http中的post请求。
    • destroy()
      用于结束时的状态改变或者资源回收等。

    2.4 web.xml文件如下:

    <web-app>
        <servlet>
            <servlet-name>servletdemo</servlet-name>
            <servlet-class>cn.shinysw.servlet.Servlet1</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>servletdemo</servlet-name>
            <url-pattern>/haha</url-pattern>
        </servlet-mapping>
    </web-app>
    

    这个文件,是实现url到servlet的具体类之间的映射关系的关键。我们通过<servlet>定义一个具体的处理容器。这个容器一般包括两部分:

    • 具体类 -> servlet 之间的关联
      <servlet>
          <servlet-name>servletdemo</servlet-name>
          <servlet-class>cn.shinysw.servlet.Servlet1</servlet-class>
      </servlet>
      
    • url -> servlet 之间的关联
      <servlet-mapping>
          <servlet-name>servletdemo</servlet-name>
          <url-pattern>/haha</url-pattern>
      </servlet-mapping>
      

    首先,我们通过定义一个<servlet>把具体的HttpServlet的处理类 cn.shinysw.servlet.Servlet1 绑定到 servletdemo 上面,这个名称可以是任意的,只要不与其他的冲突就可以了。然后,我们通过一个<servlet-mapping>把一个url-pattern /haha 绑定到 servletdemo 上面,这里的url-pattern可以是glob形式的匹配模式,比如 /* /test* 等。这样,当我们在浏览器中输入服务器的ip+端口号+项目名称+url-pattern能够匹配到的url,都会发送给绑定的类进行处理了。这里,假设我们使用jetty或者tomcat启动这个war,程序使用默认端口8080,那么当我们访问 http://localhost:8080/servletdemo/haha 时,服务器就会把这个http请求发送给绑定的 cn.shinysw.servlet.Servlet1 这个类进行处理。
    注意,这里url-pattern,不管前面是否带有 / ,都是相对于项目url的,所以, /hahahaha 效果一样。对于Jetty容器,可以省略掉/,但是tomcat不能省略,省略掉会报错,无法启动。

    3 http GET 请求

    当我们在url后面,添加?key1=value1&key2=value2这种形式的请求时,就是发送GET请求。这种请求,由于参数都是可见的,所以,当参数包含密码,以及敏感信息等时,这种方式是不合适的。另外,这种方式,参数最多只能包含1024个字节,所以,长度比较长的参数也不适合用GET请求。相反,像淘宝的item,这种希望能够通过收藏夹收藏链接,下次直接定位到item等,就特别适合于使用GET,将item信息存储在url中。
    另一种发送GET请求的方法是使用form,我们在webapp中创建一个get.html,在其中输入:

    <!doctype html>
    <html>
        <body>
            <form action="para" method="GET">
                first name: <input name="first_name" type="text" value="yang"/>
                <br/>
                <input  type="submit" value="Submit"/>
            </form>
        </body>
    </html>
    

    其中的action指向的就是我们在web.xml中定义的url-pattern。
    注意:这个action的起始地址是相对于当前项目对应的url,不应带 / 。比如这个例子,是相对于 http://localhost:8080/servletdemo 的。所以,action="para",最终调用的url是 http://localhost:8080/servletdemo/para 。而假设我们使用action="/para",则最终调用的url是 http://localhost:8080/para ,则会出现404错误。
    同样,我们创建一个新的Servlet类来处理这个请求:

    package cn.shinysw.servlet;
    
    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    public class Servlet2 extends HttpServlet {
        public void doGet(HttpServletRequest request,
                          HttpServletResponse response)
            throws ServletException, IOException {
            System.out.println(request.getParameter("first_name"));
    
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
    
            String title = "Http GET paramater:";
    
            out.println("<!doctype html>");
            out.println("<html>");
            out.println("<body bgcolor="#241341">");
            out.println("<h1>" + "" + title + "</h1>");
            out.println("<p>" + request.getParameter("first_name") + "</p>");
            out.println("</body>");
            out.println("</html>");
        }
    }
    

    然后,在web.xml中增加一个mapping:

    <servlet>
        <servlet-name>s2</servlet-name>
        <servlet-class>cn.shinysw.servlet.Servlet2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>s2</servlet-name>
        <url-pattern>/para</url-pattern>
    </servlet-mapping>
    

    4 http POST 请求

    由于GET请求的参数是可见的,所以,为了安全,我们常常需要使用POST请求,POST请求常使用form的形式,和GET一样的。
    在webapp下创建post.html文件:

    <!doctype html>
    <html>
        <body>
            <form action="post" method="POST">
                first name: <input name="first_name" type="text" value="yang"/>
                <br/>
                <input  type="submit" value="Submit"/>
            </form>
        </body>
    </html>
    

    同样,我们创建一个新的Servlet类来处理这个请求:

    package cn.shinysw.servlet;
    
    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    public class Servlet3 extends HttpServlet {
        public void doGet(HttpServletRequest request,
                          HttpServletResponse response)
            throws ServletException, IOException {
            System.out.println(request.getParameter("first_name"));
    
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
    
            String title = "Http GET paramater:";
    
            out.println("<!doctype html>");
            out.println("<html>");
            out.println("<body bgcolor="#241341">");
            out.println("<h1>" + "" + title + "</h1>");
            out.println("<p>" + request.getParameter("first_name") + "</p>");
            out.println("</body>");
            out.println("</html>");
        }
        public void doPost(HttpServletRequest request,
                           HttpServletResponse response)
            throws ServletException, IOException {
            doGet(request, response);
        }
    }
    

    然后,在web.xml中增加一个mapping:

    <servlet>
        <servlet-name>s3</servlet-name>
        <servlet-class>cn.shinysw.servlet.Servlet3</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>s3</servlet-name>
        <url-pattern>post</url-pattern>
    </servlet-mapping>
    

    5 Filter

    Filter顾名思义就是做过滤工作的。实际上,可以将它理解为一种装饰器。当需要的时候,可以在操作之前添加一些操作行为。这些操作完全可以放在每个单独的servlet当中去。之所以弄出一个Filter来,完全是因为这些工作大多数是共用的,如果每个servlet当中去写一个,就会重复。所以,干脆把它单独出来,通过配置的方式,哪些页面需要这些处理的,就加一下。然后,根据处理的内容,还可以分类,filter1,filter2…,哪些页面需要几个filter,自己去组合,组合的顺序,是在web.xml中的定义顺序决定的。

    <filter>
        <filter-name>f1</filter-name>
        <filter-class>cn.shinysw.servlet.ServletFilter1</filter-class>
        <init-param>
            <param-name>test_param</param-name>
            <param-value>Initialization Paramter Demo</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>f1</filter-name>
        <url-pattern>*</url-pattern>
    </filter-mapping>
    

    6 ErrorHandler

    <error-page>
        <expecption-type>java.lang.Throwable</expecption-type>
        <location>/errorpage</location>
    </error-page>
    

    其中的location必须带 / ,但这个location不管带不带 / 都是相对于项目url的。

    7 Cookie

    Cookie是服务器将一些常用的信息保存在浏览器上的一种方法。好比,一家餐馆,它想做活动,发给顾客一张凭证,每吃一次盖一个章,盖满5次送一顿饭。而这张凭证,相当于是餐馆给顾客发的一个cookie。cookie的好处就是,餐馆不需要保管这些凭证,只需要顾客自己保管好就行了。缺点是,顾客可能自己伪造印章,自己盖章,安全性较差。相应的,后面会介绍session,session是与cookie相反的一种存储信息的方式。它相当于,同样是餐馆做活动,这次它不发凭证给顾客,而是给顾客办个会员卡,分配一个会员号。以后,每次来只需要会员号就行了,吃饭的次数保存在餐馆的记录本(数据库/服务器)里面。这种方式的优点是,由于数据是在餐馆里面,所以安全性比较高。缺点是,需要餐馆来进行数据的管理,增加管理成本,特别是涉及到多家连锁餐馆的时候,这些数据是集中保存还是分布式保存,分布式保存时如何同步等,会比较麻烦。

    package cn.shinysw.servlet;
    
    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    public class ServletCookie extends HttpServlet {
        public void doGet(HttpServletRequest request,
                          HttpServletResponse response)
            throws ServletException, IOException {
            response.setContentType("text/html");
            response.setCharacterEncoding("utf-8");
            PrintWriter out = response.getWriter();
    
            Cookie cookie = null;
            Cookie[] cookies = null;
            Boolean flagExistVisit = false;
    
            cookies = request.getCookies();
            for (Cookie c : cookies) {
                if (c.getName().equals("visit_times")) {
                    c.setValue("" + ((new Integer(c.getValue())).intValue() + 1));
                    response.addCookie(c);
                    cookie = c;
                    flagExistVisit = true;
                    break;
                }
            }
    
            response.addCookie(new Cookie("first_name", "yang"));
            for (Cookie c : cookies) {
                if (c.getName().equals("first_name")) {
                    c.setMaxAge(0);
                    response.addCookie(c);
                    out.println(c.getName() + ": " + c.getValue() + "<br/>");
                    break;
                }
            }
    
            if (!flagExistVisit) {
                cookie = new Cookie("visit_times", "1");
                cookie.setMaxAge(60*60*24);
                response.addCookie(cookie);
            }
    
            out.println("中文 访问次数 visit times: " + cookie.getValue());
        }
    
        public void doPost(HttpServletRequest request,
                           HttpServletResponse response)
            throws ServletException, IOException {
            doGet(request, response);
        }
    }
    

    这里顺便测试了一下中文的输出,由于默认编码是iso-8859的,所以,中文是毫无疑问会乱码的。那么,添加:

    response.setCharacterEncoding("utf-8")
    

    之后,是否能输出中文了呢?发现,还是乱码的,并且,检查java源文件的编码也是utf-8。那么,只有可能是javac在编译的时候,用的不是utf-8编码的。一般,编译器都会使用本地系统的编码,我们在windows系统中,当然就是使用gb2312了或者cp936。将文件编码改为gb2312, response.setCharacterEncoding("gb2312") ,然后,编译运行,果然就能正确地输出中文了。当然,为了更通用,我们还是改为utf-8编码。那么,就需要改变javac的编码了。我们在build.gradle添加:

    compileJava.options.encoding = 'utf-8'
    

    8 原理总结

    1. Servlet是一种规范,称为Servlet规范,是J2EE规范的一部分。
    2. Servlet规范定义了Servlet相关的一组接口、其实现是由Servlet容器开发商来实现,类似于JDBC驱动。
    3. Servlet的也是类,其对象是通过Servlet容器来创建,Servlet只能在Servlet容器中运行。打个比方说:容器是青山,Servlet是松柏。
    4. 当客户端请求Servlet时,容器会做两件事情:
      a. Servlet容器会将请求自动组装为一个ServletRequest对象,并自动产生一个ServletResponse对象,这两个对象一并传递给Servlet的service(request,response)方法。
      b. 在该Servlet对象上调用service(request,response)方法来处理并响应用户的请求。
    5. 用户无法直接调用Servlet的方法,也无法去创建Servlet的实例。

    Date: 2017-05-09 16:01

    Created: 2017-05-25 周四 10:25

    Emacs 26.0.50.4 (Org mode 8.2.10)

    Validate

  • 相关阅读:
    遗产继承骗局
    Nginx配置https和wss
    Servlet乱码问题
    logback+slf4j作为日志系统
    InteliJ Idea通过maven创建webapp
    pip安装注意事项
    [转]TCP(HTTP)长连接和短连接区别和怎样维护长连接
    Python Socket编程初探
    有用的git片段
    Python使用chardet包自动检测编码
  • 原文地址:https://www.cnblogs.com/yangwen0228/p/6902387.html
Copyright © 2020-2023  润新知