一、servlet简介
Servlet技术是基于Java编程语言的WEB服务器端编程技术。
Servlet技术也是JSP技术(另外一种动态网页开发技术)的基础。
一个Servlet程序就是一个实现了特殊接口的 Java类,用于被支持Servlet的WEB服务器调用和运行,即只能运行于具有Servlet引擎的WEB服务器端。
Servlet与普通java程序相比,只是输入信息的来源和输出结果的目标不一样,所以,普通Java程序所能完成的大多数任务,Servlet程序都可以完成。Servlet程序具有如下的一些基本功能:
1.获取客户端通过HTML的FORM表单递交的数据和URL后面的参数信息;
2.创建对客户端的响应消息内容;
3.访问服务器端的文件系统;
4.连接数据库并开发基于数据库的应用;
5.调用其它的Java类。
二、编写第一个servlet
servlet必须实现javax.servlet.Servlet接口,Servlet接口定义了Servlet容器与Servlet程序之间通信的协议约定。
为了简化Servlet程序的编写,Servlet API中也提供了一个实现了Servlet接口的最简单的Servlet类,其完整名称为javax.servlet.GenericServlet,这个类实现了Servlet程序的基本特征和功能。
Servlet API中还提供了一个专用于HTTP协议的Servlet类,其名称是javax.servlet.http.HttpServlet,它是GenericServlet的子类,在GenericServlet类的基础上进行了一些针对HTTP特点的扩充。
要编写一个Servet类,这个类必须继承GenericServlet类或HttpServlet类。为了充分利用HTTP协议的功能,在一般情况下,都应让自己编写的Servlet类继承HttpServlet类,而不是继承GenericServlet类。
下面编写我们第一个servlet程序:
HelloWorldServlet.java
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorldServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{ response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>Hello World!</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Hello World!</h1>"); out.println("</body>"); out.println("</html>"); } }
web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <display-name>Servlet HelloWorld</display-name> <description> Servlet HelloWorld </description> <servlet> <servlet-name>helloWorldServlet</servlet-name> <servlet-class>cn.heimar.HelloWorldServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloWorldServlet</servlet-name> <url-pattern>/helloworld</url-pattern> </servlet-mapping> </web-app>
启动tomcat服务器,输入地址:http://localhost:8080/servlet/helloworld,结果在页面上出现Hello world。
三、servlet生命周期
Servlet生命周期分为三个阶段,分别用servlet三个方法代表:
初始化阶段,调用init()方法。
响应客户请求阶段,调用service()方法。
终止阶段,调用destroy()方法。
在servlet生命周期中,servlet的初始化和和销毁阶段只会发生一次,而service方法执行的次数则取决于servlet被客户端访问的次数。
1. Servlet初始化阶段
在下列时刻Servlet容器装载Servlet:
Servlet容器启动时自动装载某些Servlet,实现它只需要在web.XML文件中的<Servlet></Servlet>之间添加如下代码:
<loadon-startup>1</loadon-startup>
在Servlet容器启动后,客户首次向Servlet发送请求。
Servlet类文件被更新后,重新装载Servlet。
Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。在Servlet的整个生命周期中,init()方法只被调用一次。
2.响应客户请求阶段
对于用户到达Servlet的请求,Servlet容器会创建特定于这个请求的ServletRequest对象和ServletResponse对象,然后调用Servlet的service方法。service方法从ServletRequest对象获得客户请求信息,处理该请求,并通过ServletResponse对象向客户返回响应信息。
3.终止阶段
当WEB应用被终止,或Servlet容器终止运行,或Servlet容器重新装载Servlet新实例时,servlet容器会先调用servlet对象的destrory方法,然后再销毁servlet对象,同时也会销毁与servlet对象相关联的servletConfig对象。
正常关闭Tomcat服务器时,才会执行servlet的destroy()方法。所以一般重要的代码不要放在此方法中。
四、web.xml详解
详细资料请参考/java/web-xml-wen-jian-xiang-jie/
五、会话跟踪
在 Servlet 规范中,常用以下两种机制完成会话跟踪:
使用持续的Cookie。
使用Servlet API中Session(会话)机制。
cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。
由于服务器端保持状态的方案在客户端也需要保存一个标识,所有session机制可能需要借助于cookie机制来达到保存标识的目的。但实际上还有其他选择。
1. Cookie
1.1 Cookie概述
Cookie分为会话Cookie和持久化Cookie。
Cookie是一种在客户端保持HTTP状态信息的技术。
一个Cookie只能记录一种信息,它至少含有一个标识信息的名称(NAME)和设置值(VALUE)(key-value)。
浏览器一般最多能存入300个Cookie,每个站点的Cookie最多可以放20个Cookie,每个Cookie大小限制在4K。
如果不设置过期时间,则表示这个cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不保存在硬盘上而是保存在内存里。持久化Cookie保存在硬盘上。
1.2 Cookie具体使用
设置Cookie:
Cookie cookie = new Cookie(“userName”,username); response.addCookie(cookie);
获得Cookie:
Cookie[] cookies = request.getCookies();
默认情况下,不设置cookie的超时时间,那么超时时间就是关闭浏览器时。可以通过setMaxage()方法来规定cookie超时时间,单位为秒。
cookie.setMaxage(60);
删除cookie方法:
cookie.serMaxage(0);
重新设置cookie的值,需要重新添加cookie。对cookie重新设置后一般都需要重新添加。
cookie.setVlaue(“123”); response.addCookie(cookie);
Firefox查看本地计算机硬盘上的cookies方式:浏览器/工具/选项/隐私。
IE查看本地计算机硬盘上的cookies方式:Internet选项/工具/设置/查看文件。
1.3 Cookie缺陷
不安全。
只能传送String类型。大小数量有限制。
2.Session
2.1 从当前请求中得到一个session对象。
注意:如果当前请求没有绑定一个session,那么为当前会话创建一个session对象,否则直接得到当前会话绑定的session对象。
2.2 直接失效session,在下次请求此次sessionID会失效,但是请求后会重新设置一个sessionID。
2.3 session中的key一般大写。如:USER_IN_SESSION
2.4 session是运行在服务器端的,如果用户访问后不在访问服务器,则造成资源浪费。所以一般需要设置session两次请求之间的最大超时时间。秒为单位。
session.setMaxInactiveInterval(int );
2.5 删除session中的一个参数。
session.removeArrtibute(“USER_IN_SESSION”);
2.6 销毁当前session。
Session.invalidate();
2.7 在web.xml中设置所有的session超时。
<session-config> <session-timeout>30</session-timeout> </session-config>
2.8 使用UUID可以简单方便生成随机字符串。UUID(通用唯一标识符)。
2.9 在写需要放到session中的对象的时候,最后要实现Serizlizable接口。
2.10 session的钝化和锐化。
2.11保存session id的三种方式
(1)保存session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。
(2)由于cookie可以被人为的禁止,必须有其它的机制以便在cookie被禁止时仍然能够把session id传递回服务器,经常采用的一种技术叫做 URL重写,就是把session id附加在URL路径的后面,附加的方式也有两种:
一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。
(3)另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单:
<form name="testform" action="/xxx"> <input type="text"> </form>
在被传递给客户端之前将被改写成:
<form name="testform" action="/xxx"> <input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764"> <input type="text"> </form>
这种技术现在已较少应用。
更多cookie和session资料请参考http://www.360doc.com/content/11/1227/00/198_175229712.shtml
六、Servlet之间的通信
通信方式:共享信息和共享控制。
1. 共享控制
三种方式:
1.1 请求转发(forward)
Servlet使用javax.servlet.RequestDispather.forward(“里面写servlet-mapping的内容”)方法来转发它所受到的http请求。Path参数指定要转发的目标servlet或者jsp,必须从“/”开始,这个“/”代表应用的根目录。
如:
RequestDispatcher rd = req.getRequestDispatcher(“/servlet”); rd.forWard(req.resp);
如果已经通过响应放回一个servletOutputStream对象或者PrintWriter对象,这个方法将不能使用。
此种方式可以访问WEB-INF下的资源。
1.2 URL重定向(tedirect)
如:
resp.sendRedirect(req.getContextPath()+”/servlet”);
此种方式不能访问WEB-IN下的资源。
1.3 包含(include)
如:
RequestDispatcher rd = req.getRequestDispatcher(“/servlet”); rd.include(req.resp);
2. 共享信息
四个作用域(四个共享对象):
pageContext当前页面
request一次请求
session一次会话
ServletContext整个web的生命周期(存放全局变量)
七、Servlet的线程安全问题
Servlet引擎采用多线程模式运行,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。
解决方案:
1. 实现 SingleThreadModel 接口。实现这个接口并不能真正解决servlet的线程安全问题,因为servlet引擎会创建多个servlet实例对象。如果一个Servlet实现了SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销。事实上,在servlet API2.4中,已经在它标记为Deprecated(过时的)。
2. 同步对共享数据的操作。使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段。此种方式会使系统性能大大降低。
3. 避免使用实例变量。只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。此种方式是保证Servlet线程安全的最佳选择。