Servlet学习笔记

Servlet概述

  • Servlet(Server Applet),全称Java Servlet,暂无中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
  • Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
    最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
  • 实现方式
    1. 实现servlet接口
    2. 继承genericservlet
    3. 通过继承httpservlet开发servlet

Servlet规范

  • 一个最基本的 Java Web 项目所需的 jar 包只需要一个 servlet-api.jar ,这个 jar 包中的类大部分都是接口,还有一些工具类,共有 2 个包,分别是 javax.servlet 和 javax.servlet.http。所有的 Servlet 容器都带有这个包,你无需再放到Web项目里,放到这里只不过是编译的需要,运行是不需要的。如果你硬是把 servlet-api.jar 放到 webapp/WEB-INF/lib 目录下,那么 Tomcat 启动时还会报一个警告信息。

  • Servlet 是 J2EE 最重要的一部分,有了 Servlet 就是 J2EE 了,J2EE 的其他方面的内容择需采用。而 Servlet 规范需要掌握的就是 servlet 和 filter 这两项技术。绝大多数框架不是基于 servlet 就是基于 filter,如果它要在 Servlet 容器上运行,就永远也脱离不开这个模型。

  • 为什么 Servlet 规范会有两个包,javax.servlet 和 javax.servlet.http?
    早先设计该规范的人认为 Servlet 是一种服务模型,不一定是依赖某种网络协议之上,因此就抽象出了一个 javax.servlet ,同时在提供一个基于 HTTP 协议上的接口扩展。但是从实际运行这么多年来看,似乎没有发现有在其他协议上实现的 Servlet 技术。

  • Servlet 规范其实就是对 HTTP 协议做面向对象的封装,HTTP协议中的请求和响应就是对应了HttpServletRequest 和 HttpServletResponse 这两个接口。

    可以通过 HttpServletRequest 来获取所有请求相关的信息,包括 URI、Cookie、Header、请求参数等等,别无它路。因此当你使用某个框架时,你想获取HTTP请求的相关信息,只要拿到 HttpServletRequest 实例即可。

    而 HttpServletResponse接口是用来生产 HTTP 回应,包含 Cookie、Header 以及回应的内容等等。HTTP 协议里是没有关于 Session 会话的定义,Session 是各种编程语言根据 HTTP 协议的无状态这种特点而产生的。其实现无非就是服务器端的一个哈希表,哈希表的Key就是传递给浏览器的名为 jsessionid 的 Cookie 值。

  • 当需要将某个值保存到 session 时,容器会执行如下几步:

    a. 获取 jsessionid 值,没有的话就生成一个,也就是 request.getSession() 这个方法

    b. 拿到的 HttpSession 对象实例就相当于一个哈希表,你可以往哈希表里存放数据(setAttribute)

    c. 也可以通过 getAttribute 来获取某个值。而这个名为 jsessionid 的 Cookie 在浏览器关闭时会自动删除。把 Cookie 的 MaxAge 值设为 -1 就能达到浏览器关闭自动删除的效果。

  • 在 servlet 中有一个包 javax.servlet.jsp 是跟 JSP 相关的一些接口规范定义。JSP 比 Servlet 方便的地方在于可直接修改立即生效,不像 Servlet 修改后必须重启容器才能生效。 因此 JSP 适合用来做视图,而 Servlet 则适合做控制层。

Servlet线程安全

  • servlet中默认线程不安全,单例多线程,因此对于共享的数据(静态变量,堆中的对象实例等)自己维护进行同步控制,不要在service方法或doGet等由service分派出去的方法,直接使用synchronized方法,很显然要根据业务控制同步控制块的大小进行细粒度的控制,将不影响线程安全的耗时操作移出同步控制块。

  • 针对Servlet的线程安全问题,Sun公司是提供有解决方案的:让Servlet去实现一个SingleThreadModel接口,如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。

  • 查看Sevlet的API可以看到,SingleThreadModel接口中没有定义任何方法和常量,在Java中,把没有定义任何方法和常量的接口称之为标记接口,经常看到的一个最典型的标记接口就是”Serializable”,这个接口也是没有定义任何方法和常量的,标记接口在Java中有什么用呢?主要作用就是给某个对象打上一个标志,告诉JVM,这个对象可以做什么,比如实现了”Serializable”接口的类的对象就可以被序列化,还有一个”Cloneable”接口,这个也是一个标记接口,在默认情况下,Java中的对象是不允许被克隆的,就像现实生活中的人一样,不允许克隆,但是只要实现了”Cloneable”接口,那么对象就可以被克隆了。

    让Servlet实现了SingleThreadModel接口,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。

  • 对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。

  • 实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。

知识点

web.xml配置

标签结构

  1. <web-app>
  2. <!--Servlet标签-->
  3. <servlet>
  4. <servlet-name>Servlet名称</servlet-name>
  5. <servlet-class>包名+类名</servlet-class>
  6. </servlet>
  7. <!--Servlet映射-->
  8. <servlet-mapping>
  9. <servlet-name>Servlet名称</servlet-name>
  10. <url-pattern>URL值</url-pattern>
  11. </servlet-mapping>
  12. </web-app>

常用类

HttpServletRequest类

  • 常见方法
    • getRequestURL()
      浏览器发出请求时的完整URL,包括协议 主机名 端口(如果有)
    • getRequestURI()
      浏览器发出请求的资源名部分,去掉了协议和主机名
    • getQueryString()
      请求行中的参数部分,只能显示以get方式发出的参数,post方式的看不到
    • getRemoteAddr()
      浏览器所处于的客户机的IP地址
    • getRemoteHost()
      浏览器所处于的客户机的主机名
    • getRemotePort()
      浏览器所处于的客户机使用的网络端口
    • getLocalAddr()
      服务器的IP地址
    • getLocalName()
      服务器的主机名
    • getMethod()
      得到客户机请求方式一般是GET或者POST
  • 获取参数

    • getParameter()
      是常见的方法,用于获取单值的参数
    • getParameterValues()
      用于获取具有多值得参数,比如某些注册提交的信息是多选的。
    • getParameterMap()
      用于遍历所有的参数,并返回Map类型。
  • 获取头信息

    • getHeader()
      获取浏览器传递过来的头信息。 比如getHeader(“user-agent”) 可以获取浏览器的基本资料,这样就能判断是firefox、IE、chrome、或者是safari浏览器
    • getHeaderNames()
      获取浏览器所有的头信息名称,根据头信息名称就能遍历出所有的头信息
    • getCookies
      获取浏览器传递来的所有Cookie,返回值是Cookie[]数组
  • 服务器传参
    setAttribute和getAttribute可以用来在进行服务端跳转的时候,在不同的Servlet之间进行数据共享

HttpServletRespond类

  • 设置响应内容
    通过getWriter()获取一个PrintWriter 对象
    可以使用println(),append(),write(),format()等等方法设置返回给浏览器的html内容。
  • 设置响应格式
    通过setContentType()设置格式,对应头信息HttpServletRequest.getHeader(“accept”)。浏览器不能识别,那么打开此servlet就会弹出一个下载的对话框。
  • 设置响应编码
    设置响应编码有两种方式,这两种方式都需要在HttpServletRespond.getWriter调用之前执行才能生效。

    • setContentType(“text/html; charset=UTF-8”);
      不仅发送到浏览器的内容会使用UTF-8编码,而且还通知浏览器使用UTF-8编码方式进行显示。所以总能正常显示中文
    • setCharacterEncoding(“UTF-8”);
      仅仅是发送的浏览器的内容是UTF-8编码的,置于浏览器是用哪种编码方式显示不管。 所以当浏览器的显示编码方式不是UTF-8的时候,就会看到乱码,需要手动再进行一次设置。
  • 设置缓存
    使用缓存可以加快页面的加载,降低服务端的负担。但是也可能看到过时的信息,可以通过如下手段通知浏览器不要使用缓存:

    1. response.setDateHeader("Expires",0 );
    2. response.setHeader("Cache-Controll","no-cache");
    3. response.setHeader("pragma","no-cache");
  • 客户端跳转
    客户端有两种跳转

    • 302 表示临时跳转
      使用sendRedirect( “XXXX.html”)方法重定向
    • 301 表示永久性跳转
      setStatus(301),setHeader(“Location”, “XXXX.html”);
  • 添加Cookie
    通过response.addCookie()方法,将cookie保存在浏览器端

Cookie

使用javax.servlet.http.Cookie类

  • 常用方法
    • setMaxAge
      设置Cookie保存时间,如果为0,表示浏览器一关闭就销毁。基础单位为秒。
    • setPath
      Path表示服务器的主机名,只有浏览器通过这个主机名访问服务器的时候才会提交这个cookie到服务端

常用方法

doPost(HttpServletRequest , HttpServletResponse )

当浏览器使用post方式提交数据的时候,servlet需要提供doPost()方法

  • post方式种类
    • 在form上显示设置 method=”post”的时候
    • ajax指定post方式的时候

doGet(HttpServletRequest , HttpServletResponse )

当浏览器使用get方式提交数据的时候,servlet需要提供doGet()方法

  • get方式种类
    • form默认的提交方式,即method=”get”的时候
    • 如果通过一个超链访问某个地址
    • 如果在地址栏直接输入某个地址
    • ajax指定使用get方式的时候

service(HttpServletRequest , HttpServletResponse )

Servlet子类除继承了HttpServlet类,同时也继承了service()方法
实际上,在执行doGet()或者doPost()之前,都会先执行service(),由service()方法进行判断,到底该调用doGet()还是doPost()
可以发现,service(), doGet(), doPost() 三种方式的参数列表都是一样的。
所以,有时候也会直接重写service()方法,在其中提供相应的服务,就不用区分到底是get还是post。

跳转

  • 服务端跳转
    request.getRequestDispatcher("XXXX.html").forward(request, response);
    服务端跳转可以看到浏览器的地址依然是原路径,并不会变成XXXX.html
  • 客户端跳转
    response.sendRedirect("XXXX.html");
    在Servlet中进行客户端跳转,即发送重定向,浏览器地址发生改变,转到XXXX.html
  • 图解

上传文件

  • 页面准备
    html文件内form标签的method必须是post的
    需要加上enctype="multipart/form-data,表示提交的数据是二进制文件
    需要提供input标签type=”file” 的字段进行上传
  • 准备上传文件的Servlet继承类
    一般需要用到commons-io和commons-fileupload这两个Apache的jar包,除导入到项目中还应存放在WEB-INF/lib目录下
    一般通过如下代码拿到Items的迭代器,遍历得到的每一个item对应为一个浏览器提交的数据

    1. String filename = null;
    2. DiskFileItemFactory factory = new DiskFileItemFactory();
    3. ServletFileUpload upload = new ServletFileUpload(factory);
    4. // 设置上传文件的大小限制为1M
    5. factory.setSizeThreshold(1024 * 1024);
    6. List<?> items = null;
    7. try
    8. {
    9. items = upload.parseRequest(request);
    10. } catch (FileUploadException e)
    11. {
    12. e.printStackTrace();
    13. }
    14. Iterator<?> iter = items.iterator();
    15. while (iter.hasNext())
    16. {
    17. FileItem item = (FileItem) iter.next();
    18. // isFormField()来判断是否是常规字段还是提交的文件。 返回true的时候,就表示是常规字段。false则为文件
    19. if (!item.isFormField())
    20. {
    21. ............
    22. }
    23. }
  • 配置web.xml
    配置Servlet继承类,并将form标签的action属性名称关联到servlet-mapping下的url-pattern标签,注意servlet-name前后要一致

自启动

有的时候会有这样的业务需求: tomcat一启动,就需要执行一些初始化的代码,比如校验数据库的完整性等。但是Servlet的生命周期是在用户访问浏览器对应的路径开始的。如果没有用户的第一次访问,就无法执行相关代码。 这个时候,就需要Servlet实现自启动。即:伴随着tomcat的启动,自动启动初始化,在初始化方法init()中,就可以进行一些业务代码的工作了。

  • 设置方法:
    在web.xml中,配置Servlet的地方,增加一句<load-on-startup>1-99</load-on-startup>
    取值范围是1-99,表示优先级。配置完成后,即表明该Servlet会随着Tomcat的启动而初始化。

生命周期

一个Servlet的生命周期由 实例化,初始化,提供服务,销毁,被回收 几个步骤组成

  • 实例化
    当用户通过浏览器输入一个路径,这个路径对应的servlet被调用的时候,该Servlet就会被实例化
    无论访问了多少次HttpServlet子类,该类构造方法只会执行一次,所以Serlvet是单例的。
  • 初始化
    HttpServlet子类同时继承了init(ServletConfig)方法,init是一个实例方法,所以会在构造方法执行后执行。无论访问多少次该类,init方法仅执行一次。
  • 提供服务
    接下来就是执行service()方法,然后通过浏览器传递过来的信息进行判断,是调用doGet()还是doPost()方法,在service()中编写我们的业务代码。
  • 销毁
    接着是销毁destroy()
    在如下几种情况下,会调用destroy():

    • 该Servlet所在的web应用重新启动。serverl.xml中<Context path="/" docBase="..." debug="0" reloadable="false" />,reloadable表示有任何类发生更新时,web应用是否自动重启。当web应用自动重启的时候,destroy()方法就会被调用。
    • 关闭tomcat的时候 destroy()方法会被调用,但是这个一般都发生的很快,不易被发现
  • 被回收
    当该Servlet被销毁后,就满足垃圾回收的条件了。 当下一次垃圾回收GC来临的时候,就有可能被回收。

过滤器Filter

  • 概念
    Filter就像一个一个哨卡,用户的请求需要经过Filter,并且可以有多个过滤器

  • 实现

    1. 编写Filter类,继承Filter接口,重写doFilter、init、destroy等方法
      • doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        添加过滤条件,在方法内部通过chain.doFilter()方法设置过滤器
      • init(FilterConfig arg0)
        与Servlet需要配置自启动才会随着服务器的启动而执行相比,init()方法不一样。Filter一定会随着服务器的启动自启动。Filter是web应用非常重要的一个环节,如果Filter启动失败,或者本身有编译错误,不仅这个Filter不能使用,整个web应用会启动失败,导致用户无法访问页面
    2. 配置web.xml,标签结构如下:
      1. <web-app>
      2. <!--filter标签-->
      3. <filter>
      4. <filter-name>过滤器名</filter-name>
      5. <filter-class>报名+类名</filter-class>
      6. </filter>
      7. <!--filter映射-->
      8. <filter-mapping>
      9. <filter-name>过滤器名</filter-name>
      10. <url-pattern>需要过滤的URL(/*:表示过滤所有)</url-pattern>
      11. </filter-mapping>
      12. </web-app>

监听Listener

Listener 的作用是用于监听 web应用的创建和销毁,以及在其上attribute发生的变化。 web应用即ServletContext对象(jsp的隐式对象application)
除了对web应用的监听外,还能监听session和request的生命周期,以及他们的attribute发生的变化。

  • 监听 Context
    • 监听web应用,继承接口ServletContextListener,重写contextDestroyed、contextInitialized方法,对应当前web应用的初始化、销毁。
    • 监听web应用属性,继承接口ServletContextAttributeListener,重写attributeAdded、attributeRemoved、attributeReplaced方法,对应监听属性的增加、删除、替换。
  • 监听Session
    对Session的监听分生命周期的监听,和Session上Attribute变化的监听两种。

    • 监听Session,继承接口HttpSessionListener,重写sessionCreated、sessionDestroyed方法,对应Session会话创建和销毁的执行
    • 监听Session属性,继承接口HttpSessionAttributeListener,重写attributeAdded、attributeRemoved、attributeReplaced方法,对应监听属性的增加、删除、替换。
  • 监听Request
    对Request的监听分生命周期的监听,和Request上Attribute变化的监听两部分。

    • 监听Request,继承接口ServletRequestListener,重写sessionCreated、sessionDestroyed方法,对应Session会话创建和销毁的执行
    • 监听Request属性,继承接口ServletRequestAttributeListener,重写attributeAdded、attributeRemoved、attributeReplaced方法,对应监听属性的增加、删除、替换。
  • 配置web.xml,格式如下:

    1. <listener>
    2. <listener-class>Listener类名</listener-class>
    3. </listener>

Servlet(Filter)中的url-pattern

  • Serlvet和Filter有三种不同的匹配规则:

    • (1)精确匹配:/foo;
    • (2)路径匹配:/foo/*;
    • (3)后缀匹配:*.html;
  • Serlvet的匹配顺序是:
    首先进行精确匹配;如果不存在精确匹配的进行路径匹配;最后根据后缀进行匹配;一次请求只会匹配一个Servlet;(Filter是只要匹配成功就添加到FilterChain)
    PS:其他写法(/foo/,/.html,/foo)都不对;“/foo*”不能匹配/foo,/foox;

发表评论

电子邮件地址不会被公开。 必填项已用*标注