servlet
所有从servlet的几个对象中getAttribute的返回值都是对象Object
使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:
- 1、性能明显更好。
- 2、Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。
- 3、Servlet 是独立于平台的,因为它们是用 Java 编写的。
- 4、服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
5、Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。
二、servlet处理步骤。
编写一个类继承HttpServlet类,重写service方法,doPost或者doGet方法。实现处理逻辑,在web.xml中配置servlet
-
接收请求数据
-
处理业务逻辑
-
发送响应信息
- 读取表单数据:resquest:getParameter(str) Str[] getParameterValues(str)
-
绑定数据到request:req.setAttribute(s,s) getAttribute(s) removeAttribute(s);
-
Servlet解决乱码问题:浏览器、服务器默认的解码编码都是iso接收数据,一般把下面两个一起使用:通用:转换编码格式:new String((request.getParameter("name")).getBytes("ISO-8859-1"),"UTF-8")Get请求:在Server.xml配置文件中的第65行添加 URIEncoding=“utf-8”Post请求:在接收数据之前 设置 req.setCharacterEncoding="utf-8"发送数据:res.setContentType("text/html;charset=utf-8" ),这时发送的消息头,告诉浏览器发送的内容和解码的格式 新版用后者一个就可以保证正确。注意:应该先设置res的编码,再创建流传给浏览器
解决jsp乱码问题: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
-
JSP在部署后提供给用户使用,会经过三个阶段:
-
1 JSP生成java文件:这个阶段会使用pageEncoding所定义的编码格式进行转换
-
2 java文件生成class文件:这个阶段由服务器tomcat自动使用utf-8编码把java文件转换成字节码class文件
-
3 通过读取class文件展现给用户:这个阶段由tomcat服务器获取字节码内容,通过使用contentType所定义的编码格式展现给用户。
-
大致过程如下图:
-
- 部署到tomcat下,webapp该成该项目名 ,项目名下有web-inf、html等文件夹,web-inf下classes、lib、web.xml,src/main/java和src/main/resource下的文件会copy到的classes中,lib是该项目用的jar包
-
/web-inf/下是一个安全目录,不能通过路径访问,只能通过servlet或者springmvc或者struts转发或者jsp中的jspforward动作标签访问,jsp页面 保证安全放在web-inf下,在jsp中读取css、html等使用这些资源,<%=request.getContextPath()%>/cs/..使用外部css等,表示get的跟目录(/项目名 )
2.如何获取路径
- 项目名: req.getContextPath() /test
- 访问路径: getServletPath() /login.do
- URI: getRequestURI() /test/login.do
- URL: getRequestURL() http://localhost8080:/test/login.do
- getServletContext().getRealPath("/") D://test/ 即项目在磁盘上的地址
获取设置http头部信息:request.getHeader(String srr) response.setHeader(string str,string str) response.setIntHeader(string str,int val)
设置页面自动刷新: response.setIntHeader(“refresh”,5) 就是设置头部中的refresh字段
3.URI和URL的区别
1)狭义的理解
- 单纯的在Java项目中理解
- URI: 绝对路径
- URL: 完整路径
2)广义的理解(*)
- 在所有的WEB项目中理解
- URI: 资源的名称(刘苍松/苍老师/二蛋/松/cang#null)
- URL: 资源的真名(刘苍松)
- URI包含了URL
4.如何配置Servlet访问路径
4.1精确匹配(/abc)
- 只有"/abc"可以访问该Servlet
- 该Servlet只能处理这一个请求
适合请求总数很少的小项目
4.2通配符(/*)
- 所有的路径都可以访问该Servlet
- 该Servlet能处理所有的请求
- 适合使用一个组件处理所有的请求
4.3后缀(*.emp)
- 所有以emp为后缀的请求都可以访问该Servlet
- 该Servlet能处理很多请求
适合使用多个组件处理所有的请求
3.转发
1.把一个组件没完成的处理交给其他组件完成,比如一个Servlet接收数据,从Dao处理数据,再交给JSP显示数据
2.如何实现转发
- 绑定数据到Request对象 req.setAttribute(String name,Object obj)
- 获取转发器并通知服务器转发 req.getRequestDispatcher(String url).forward(req,res)
- 另一个组件的Request对象调取数据 req.getAttribute(String name) 返回的是Object,需要强转
这两个Servlet共享一个Request和Response对象
3 重定向:服务器建议浏览器访问另一个路径: res.sendRedirect(url)
4.转发和重定向的区别
相同点:都是处理web组件之间的跳转
区别:两个组件依赖时用转发,不依赖重定向。
## 转发的特点: ##重定向的特点
通常查询用转发,增加修改删除后重定向到查询
6 Servlet的两个重要的类:ServletConfig和ServletContext
这两个类都是读取web.xml的参数
6.1 ServletConfig,每个Servlet组件只有一个,Servlet创建的时候自动创建config,再通过init()方法传进去,配置如下:在servlet里配置,表示只有一个servlet使用。
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>web.LoginServlet</servlet-class>
<init-param>
<param-name>maxonline</param-name>
<param-value>3000</param-value>
</init-param>
</servlet>
servlet组件自带创建config:ServletConfig config = this.getServletConfig()
读写参数: config.getInitParameter("maxonline") Enumeration<String> e = config.getInitParameterNames() 获取一个枚举对象
6.2 ServletContext Servlet的上下文,是一个项目共有的对象,多个Servlet共享数据,配置方法如下,配置在web下面,表示多个servet共有:
<web-app>
<context-param>
<param-name>url</param-name>
<param-value>jdbc:MySQL://localhost:3306/4g</param-value>
</context-param>
</web-app>
Context的创建有几种方式,一种servlet自带的getServletContext() 第二种通过config.getServeltContext()获取ServletContext 第三种 session.getServletContext() 第四种 Filterconfig.getServletContext()
读取参数 context.getInitParameter("url") Enumeration<String> e = context.getInitParameterNames() 获取一个枚举对象
context.getAttribute("url") context.setAttribute("number",20) context.removeAttribute("url")
二、Servlet表单数据
在很多的情况下,我们需要在浏览器,Web服务器和后台程序之间传递数据。浏览器使用两种方法可将这些信息传递到Web服务器,分别为Get方法和Post方法。
1.1、Get方法:
Get方法向页面请求发送已编码的用户信息。页面和已编码的信息中间用?字符分隔,如下所示:
GET方法是默认的从浏览器向Web服务器传递信息的方法,它会产生一个很长的字符串,出现在浏览器的地址栏中。如果您要向服务器传递的是密码或其他的敏感信息,请不要使用GET方法.GET方法有大小限制:请求字符串中最多只能有1024个字符。
这些信息使用QUERY_STRING头传递,并可以通过QUERY_STRING环境变量访问,Servlet中使用doGet()方法处理这种类型的请求
1.2、Post方法:
另一个向后台程序传递信息的比较可靠的方法是POST方法。POST方法打包信息的方式与GET方法基本相同,但是POST 方法不是把信息作为URL中?字符后的文本字符串进行发送,而是把数据添加到实体内容中。消息以标准输出的形式传到后台程序,您可以解析和使用这些标准输出。Servlet使用doPost()方法处理这种类型的请求。
Servlet 读取表单数据
Servlet 处理表单数据,这些数据会根据不同的情况使用不同的方法自动解析:
-
getParameter():您可以调用 request.getParameter() 方法来获取表单参数的值。
-
getParameterValues():如果参数出现一次以上,则调用该方法,并返回多个值,例如复选框。
三、Servlet的生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- 1、Servlet 通过调用 init () 方法进行初始化。
- 2、Servlet 调用 service() 方法来处理客户端的请求。
- 3、Servlet 通过调用 destroy() 方法终止(结束)。
- 4、最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
init() 方法
init 方法被设计成只调用一次。服务器在接收到客户端的请求,先判断这个请求是不是第一次,是就生成一个servlet对象,服务器的jsp引擎就把jsp文件转换成字节码,不然就直接访问字节码文件, 它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像 Applet 的 init 方法一样。
Servlet 创建于用户第一次调用对应于该 Servlet 的 URL 时,但是您也可以指定 Servlet 在服务器第一次启动时被加载。在<servlet-class>后面添加<load-on-startup>1</load-on-startup>
service() 方法
service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。
destroy() 方法
destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。
三、怎样理解Servlet的单实例多线程?
不同的用户同时对同一个业务(如注册)发出请求,那这个时候容器里产生的有是几个servlet实例呢?
答案是:只有一个servlet实例。X一个servlet是在第一次被访问时加载到内存并实例化的。同样的业务请求共享一个servlet实例。不同的业务请求一般对应不同的servlet。
由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题。
JSP的中存在的多线程问题:
当客户端第一次请求某一个JSP文件时,服务端把该JSP编译成一个CLASS文件,并创建一个该类的实例,然后创建一个线程处理CLIENT端的请求。如果有多个客户端同时请求该JSP文件,则服务端会创建多个线程。每个客户端请求对应一个线程。以多线程方式执行可大大降低对系统的资源需求,提高系统的并发量及响应时间。、
对JSP中可能用的的变量说明如下:
实例变量: 实例变量是在堆中分配的,并被属于该实例的所有线程共享,所以不是线程安全的。
JSP系统提供的8个类变量
JSP中用到的OUT,REQUEST,RESPONSE,SESSION,CONFIG,PAGE,PAGECONXT是线程安全的(因为每个线程对应的request,respone对象都是不一样的,不存在共享问题), APPLICATION在整个系统内被使用,所以不是线程安全的。
局部变量: 局部变量在堆栈中分配,因为每个线程都有它自己的堆栈空间,所以是线程安全的.
静态类: 静态类不用被实例化,就可直接使用,也不是线程安全的.
外部资源: 在程序中可能会有多个线程或进程同时操作同一个资源(如:多个线程或进程同时对一个文件进行写操作).此时也要注意同步问题.
Servlet单实例多线程机制:
Servlet采用多线程来处理多个请求同时访问。servlet依赖于一个线程池来服务请求。线程池实际上是一系列的工作者线程集合。Servlet使用一个调度线程来管理工作者线程。
当容器收到一个Servlet请求,调度线程从线程池中选出一个工作者线程,将请求传递给该工作者线程,然后由该线程来执行Servlet的service方法。当这个线程正在执行的时候,容器收到另外一个请求,调度线程同样从线程池中选出另一个工作者线程来服务新的请求,容器并不关心这个请求是否访问的是同一个Servlet.当容器同时收到对同一个Servlet的多个请求的时候,那么这个Servlet的service()方法将在多线程中并发执行。
Servlet容器默认采用单实例多线程的方式来处理请求,这样减少产生Servlet实例的开销,提升了对请求的响应时间,对于Tomcat可以在server.xml中通过<Connector>元素设置线程池中线程的数目。
四、如何开发线程安全的Servlet
2、同步对共享数据的操作
使用synchronized 关键字加锁
3、避免使用实例变量
本实例中的线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。
五、servlet与jsp的区别
1.jsp经编译后就变成了Servlet.(JSP的本质就是Servlet,JVM只能识别java的类,不能识别JSP的代码,Web容器将JSP的代码编译成JVM能够识别的java类)
2.jsp更擅长表现于页面显示,servlet更擅长于逻辑控制.
3.Servlet中没有内置对象,内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到.Jsp是Servlet的一种简化,使用Jsp只需要完成程序员需要输出到客户端的内容,Jsp中的Java脚本如何镶嵌到一个类中,由Jsp容器完成。而Servlet则是个完整的Java类,这个类的Service方法用于生成对客户端的响应。
4.对于静态HTML标签,Servlet都必须使用页面输出流逐行输出
context参数配置>监听器>过滤器>serlvet,web.xml中几个配置随意,但是filter中filter-mapping必须在相应filter之后,容器根据filter-mapping的配置顺序创建过滤器,filter配置的时候注意顺序
cookie和session的使用,这文章讲的不错https://www.cnblogs.com/zhouhbing/p/4204132.html
1.Cookie
1.1. 什么是状态管理
将客户端与服务器之间多次交互当做一个整体来看待,并且将多次交互中涉及的数据保存下来,提供给后续的交互进行数据的管理即状态管理。
这里的状态指的是当前的数据,管理指的是在这个多次交互的过程中对数据的存储、修改、删除.
1.2. 状态管理两种常见模式
状态管理的过程中重要的是数据的保存,只有存下来的数据才能在多次交互中起到记录的作用,所以可以按照管理的数据的存储方式和位置的不同来区分状态管理的模式。
如果将数据存储在客户端,每次向服务器端发请求时都将存在客户端的数据随着请求发送到服务器端,修改后再发回到客户端保存的这种模式叫做Cookie。
如果将数据存储在服务器端,并且为这组数据标示一个编号,只将编号发回给客户端。当客户端向服务器发送请求时只需要将这个编号发过来,服务器端按照这个编号找到对应的数据进行管理的这种模式叫做Session——会话。
1.3什么是Cookie
如果客户端向服务器端AddServlet发送请求,遇到创建Cookie的代码时,那么一小段文本信息就会随着response响应中的头信息被传递会客户端。如图中Set-Cookie:uname=xxx就是从服务器端传递回客户端的文本信息。当文本信息到达客户端以后,会被保存在客户端的内存或硬盘上,存在内存中会随着内存的释放而消失,存在硬盘上则会保存更长的时间。
一旦客户端存有服务器发回的文本信息,那么当浏览器再次向服务器发起请求时,如请求FindServlet这个组件,那么存储的文本信息会随着请求数据包的消息头以Cookie:uname=xxx这样的形式将文本信息发送到服务器端。只要Cookie的生命周期没有结束,那么不管是存在内存还是硬盘上的信息都会在客户端向服务器端发出请求时自动的随着消息头发送过去。
1.4Cookie的工作方式
Cookie cookie = new Cookie(String name,String value); 创建cookie
response.addCookie(cookie) 发送给客户端
Cookie[] c = resquest.getCookies() request获取cookie数组
cookie.getName(); getValue() 获取cookie的键值
cookie.setValue(str) 获取cookie后更改value
cookie.setMaxAge(int age) 设置cookie的生存期,秒为单位
cookie.getPath() 获取cookie路径
cookie.setPath(str) 设置cookie的路径
1.4 cookie的路径问题
客户端存储Cookie之后,并不是针对同一个应用访问任何资源时都自动发送Cookie到服务器端,而是会进行路径的判断。只有符合路径规范的请求才会发送Cookie到服务器端。
客户端在接受Cookie时会为该Cookie记录一个默认路径,这个路径记录的是添加这个Cookie的Web组件的路径。如,当客户端向 http://localhost:8080/test/file/addCookie.jsp发送请求时创建了cookie,那么该cookie的路径就是 /test/file.
只有当访问的地址是Cookie的路径或者其子路径时,浏览器才发送Cookie到服务器端。
如,Cookie的路径是 /test/file,那么如果访问的是 /test/file/a.jsp 或者 /test/file/b/c.jsp时,都会发送Cookie。
如果访问的是 /test/d.jsp,则浏览器不会发送Cookie
1.5cookie的生存期
- 默认age<0,cookie存在内存中,浏览器关闭就结束cookie
- age=0,服务端发送cookie给客户端就结束
- age>0,cookie存在硬盘,下次打开直接发送cookie给服务器
1.6 cookie的编码问题
编码:
Cookie cookie = new Cookie("name",URLEncoder.encode("周杨","utf-8"))
解码:
String value = cookie.getValue();
value = URLDecoder.decode(value,"utf-8");
2.Session
一、Session简单介绍
在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
session 是在服务器端建立的,浏览器访问服务器会有一个sessionid,浏览器端通过sessionid定位服务器端的session,session的创建和销毁由服务器端控制。当浏览器关闭后,session还存在在服务器端,只不过你新开的浏览器去访问服务器会创建另一个session,这个时候的 sessionid已经不一样了。也就不能访问上一次的哪个session里面的内容了。 关闭浏览器sessionid失效,用不同浏览器比如之前用ie现在用360打开,sessionid也不同。同一个浏览器和子窗口共享一个sessionid。
"session的创建和销毁由服务器端控制",服务器端才有session,客户端只是通过sessionid来匹配session.
那服务器端session如何建的呢? 普通html不会创建,jsp默认是创建的,只要你访问任何一个jsp就会创建(不过只创建一次),你关闭浏览器重新访问又会创建一个,这些创建的session由服务器自己控制销毁,你也可以在服务器端代码中销毁。
直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <%@page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);
这也是JSP中隐含的session对象的来历。
<%@ page session="false"%>
不是不让页面创建Session,而是在此JSP页面无法使用session.可以减少网络数据传输.
session的生命周期:
2.Session过期
3.服务器端调用了HttpSession的invalidate()方法。
二,Session的使用
session的创建:HttpSession session = request.getSession(boolean) 默认为true,根据sessionid查找session,没找到就新建一个session,为false的时候,没
sessionid或者有sessionid但是没找到对应的session返回null。
读写session属性 : session.setAttribute(String,object) getAttribute(str) removeAttribute(str)
立即删除session :session.invalidate()
设置超时:web.xml中设置 或者 session的setMaxInActiveInterval(int seconds) 秒为单位
浏览器禁用cookie,重写url, 就是把session id直接附加在URL路径的后面。
response.encodeRedirectURL(java.lang.String url) 用于对sendRedirect方法后的url地址进行重写。
response.encodeURL(java.lang.String url)用于对表单action和超链接的url地址进行重写,把sessionid加在url后面传给服务器。
session.getId() session.isNew()
三、session实现原理
3.1、服务器是如何实现一个session为一个用户浏览器服务的?
服务器创建session出来后,会把session的id号,以cookie的形式回写给客户机,这样,只要客户机的浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户机浏览器带session id过来了,就会使用内存中与之对应的session为之服务。可以用如下的代码证明:
1 package xdp.gacl.session; 2 3 import java.io.IOException; 4 import javax.servlet.ServletException; 5 import javax.servlet.http.HttpServlet; 6 import javax.servlet.http.HttpServletRequest; 7 import javax.servlet.http.HttpServletResponse; 8 import javax.servlet.http.HttpSession; 9 10 public class SessionDemo1 extends HttpServlet {11 12 public void doGet(HttpServletRequest request, HttpServletResponse response)13 throws ServletException, IOException {14 15 response.setCharacterEncoding("UTF=8");16 response.setContentType("text/html;charset=UTF-8");17 //使用request对象的getSession()获取session,如果session不存在则创建一个18 HttpSession session = request.getSession();19 //将数据存储到session中20 session.setAttribute("data", "孤傲苍狼");21 //获取session的Id22 String sessionId = session.getId();23 //判断session是不是新创建的24 if (session.isNew()) {25 response.getWriter().print("session创建成功,session的id是:"+sessionId);26 }else {27 response.getWriter().print("服务器已经存在该session了,session的id是:"+sessionId);28 }29 }30 31 public void doPost(HttpServletRequest request, HttpServletResponse response)32 throws ServletException, IOException {33 doGet(request, response);34 }35 }
第一次访问时,服务器会创建一个新的sesion。
点击刷新按钮,再次请求服务器,此时就可以看到浏览器再请求服务器时,会把存储到cookie中的session的Id一起传递到服务器端了,如下图所示:
我猜想request.getSession()方法内部新创建了Session之后一定是做了如下的处理
1 //获取session的Id2 String sessionId = session.getId();3 //将session的Id存储到名字为JSESSIONID的cookie中4 Cookie cookie = new Cookie("JSESSIONID", sessionId);5 //设置cookie的有效路径6 cookie.setPath(request.getContextPath());7 response.addCookie(cookie);
四、浏览器禁用Cookie后的session处理
4.1、IE8禁用cookie
工具->internet选项->隐私->设置->将滑轴拉到最顶上(阻止所有cookies)
4.2、解决方案:URL重写
response.encodeRedirectURL(java.lang.String url) 用于对sendRedirect方法后的url地址进行重写。
response.encodeURL(java.lang.String url)用于对表单action和超链接的url地址进行重写,把sessionid加在url后面传给服务器。
4.3、范例:禁用Cookie后servlet共享Session中的数据
IndexServlet
1 package xdp.gacl.session; 2 3 import java.io.IOException; 4 import java.io.PrintWriter; 5 import java.util.LinkedHashMap; 6 import java.util.Map; 7 import java.util.Set; 8 import javax.servlet.ServletException; 9 import javax.servlet.http.HttpServlet;10 import javax.servlet.http.HttpServletRequest;11 import javax.servlet.http.HttpServletResponse;12 13 //首页:列出所有书14 public class IndexServlet extends HttpServlet {15 16 public void doGet(HttpServletRequest request, HttpServletResponse response)17 throws ServletException, IOException {18 19 response.setContentType("text/html;charset=UTF-8");20 PrintWriter out = response.getWriter();21 //创建Session22
request.getSession();23 out.write("本网站有如下书:<br/>");24
Set<Map.Entry<String,Book>> set = DB.getAll().entrySet();25
for(Map.Entry<String,Book> me : set){
26 Book book = me.getValue();27 String url =request.getContextPath()+ "/servlet/BuyServlet?id=" + book.getId();
28//response. encodeURL(java.lang.String url)用于对表单action和超链接的url地址进行重写29
url = response.encodeURL(url);//将超链接的url地址进行重写
30 out.println(book.getName() + " <a href='"+url+"'>购买</a><br/>");
31 }32 }33
34public void doPost(HttpServletRequest request, HttpServletResponse response)35throws ServletException, IOException {36
doGet(request, response);37 }38}394041/**42 * @author gacl43 * 模拟数据库44*/45class DB{46privatestatic Map<String,Book> map = new LinkedHashMap<String,Book>();47static{48 map.put("1", new Book("1","javaweb开发"));49 map.put("2", new Book("2","spring开发"));50 map.put("3", new Book("3","hibernate开发"));51 map.put("4", new Book("4","struts开发"));52 map.put("5", new Book("5","ajax开发"));53 }5455publicstatic Map<String,Book> getAll(){56return map;57 }58}5960class Book{6162private String id;63private String name;6465public Book() {66super();67 }68public Book(String id, String name) {69super();70this.id = id;71this.name = name;72 }73public String getId() {74return id;75 }76publicvoid setId(String id) {77this.id = id;78 }79public String getName() {80return name;81 }82publicvoid setName(String name) {83this.name = name;84 }85 }
BuyServlet
1 package xdp.gacl.session; 2 3 import java.io.IOException; 4 import java.util.ArrayList; 5 import java.util.List; 6 import javax.servlet.ServletException; 7 import javax.servlet.http.HttpServlet; 8 import javax.servlet.http.HttpServletRequest; 9 import javax.servlet.http.HttpServletResponse;10 import javax.servlet.http.HttpSession;11 12 public class BuyServlet extends HttpServlet {13 14 public void doGet(HttpServletRequest request, HttpServletResponse response)15 throws ServletException, IOException {16 String id = request.getParameter("id");17 Book book = DB.getAll().get(id); //得到用户想买的书18 HttpSession session = request.getSession();19 List<Book> list = (List) session.getAttribute("list"); //得到用户用于保存所有书的容器20 if(list==null){21 list = new ArrayList<Book>();22 session.setAttribute("list", list);23 }24 list.add(book);25 //response. encodeRedirectURL(java.lang.String url)用于对sendRedirect方法后的url地址进行重写26 String url = response.encodeRedirectURL(request.getContextPath()+"/servlet/ListCartServlet");27 System.out.println(url);28 response.sendRedirect(url);29 }30 31 public void doPost(HttpServletRequest request, HttpServletResponse response)32 throws ServletException, IOException {33 doGet(request, response);34 }35 36 }
ListCartServlet
1 package xdp.gacl.session; 2 3 import java.io.IOException; 4 import java.io.PrintWriter; 5 import java.util.List; 6 import javax.servlet.ServletException; 7 import javax.servlet.http.HttpServlet; 8 import javax.servlet.http.HttpServletRequest; 9 import javax.servlet.http.HttpServletResponse;10 import javax.servlet.http.HttpSession;11 12 public class ListCartServlet extends HttpServlet {13 14 public void doGet(HttpServletRequest request, HttpServletResponse response)15 throws ServletException, IOException {16 response.setContentType("text/html;charset=UTF-8");17 PrintWriter out = response.getWriter();18 HttpSession session = request.getSession();19 List<Book> list = (List) session.getAttribute("list");20 if(list==null || list.size()==0){21 out.write("对不起,您还没有购买任何商品!!");22 return;23 }24 25 //显示用户买过的商品26 out.write("您买过如下商品:<br>");27 for(Book book : list){28 out.write(book.getName() + "<br/>");29 }30 }31 32 public void doPost(HttpServletRequest request, HttpServletResponse response)33 throws ServletException, IOException {34 doGet(request, response);35 }36 }
在禁用了cookie的IE8下的运行效果如下:
通过查看IndexServlet生成的html代码可以看到,每一个超链接后面都带上了session的Id,如下所示
1 本网站有如下书:<br/>javaweb开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=1'>购买</a><br/>2 spring开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=2'>购买</a><br/>3 hibernate开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=3'>购买</a><br/>4 struts开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=4'>购买</a><br/>5 ajax开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=5'>购买</a><br/>
所以,当浏览器禁用了cookie后,就可以用URL重写这种解决方案解决Session数据共享问题。而且response. encodeRedirectURL(java.lang.String url) 和response. encodeURL(java.lang.String url)是两个非常智能的方法,当检测到浏览器没有禁用cookie时,那么就不进行URL重写了。我们在没有禁用cookie的火狐浏览器下访问,效果如下:
从演示动画中可以看到,浏览器第一次访问时,服务器创建Session,然后将Session的Id以Cookie的形式发送回给浏览器,response. encodeURL(java.lang.String url)方法也将URL进行了重写,当点击刷新按钮第二次访问,由于火狐浏览器没有禁用cookie,所以第二次访问时带上了cookie,此时服务器就可以知道当前的客户端浏览器并没有禁用cookie,那么就通知response. encodeURL(java.lang.String url)方法不用将URL进行重写了。
五、session对象的创建和销毁时机
5.1、session对象的创建时机
在程序中第一次调用request.getSession()方法时就会创建一个新的Session,可以用isNew()方法来判断Session是不是新创建的
范例:创建session
1 //使用request对象的getSession()获取session,如果session不存在则创建一个 2 HttpSession session = request.getSession(); 3 //获取session的Id 4 String sessionId = session.getId(); 5 //判断session是不是新创建的 6 if (session.isNew()) { 7 response.getWriter().print("session创建成功,session的id是:"+sessionId); 8 }else { 9 response.getWriter().print("服务器已经存在session,session的id是:"+sessionId);10 }
5.2、session对象的销毁时机
session对象默认30分钟没有使用,则服务器会自动销毁session,在web.xml文件中可以手工配置session的失效时间,也可以设置session的setMaxInActiveInterval(int seconds)例如:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.5" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 6 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 7 <display-name></display-name> 8 9 <welcome-file-list>10 <welcome-file>index.jsp</welcome-file>11 </welcome-file-list>12 13 <!-- 设置Session的有效时间:以分钟为单位-->14 <session-config>15 <session-timeout>15</session-timeout>16 </session-config>17 18 </web-app>
当需要在程序中手动设置Session失效时,可以手工调用session.invalidate方法,摧毁session。
1 HttpSession session = request.getSession();
2 //手工调用session.invalidate方法,摧毁session3 session.invalidate();
3.过滤器
1、Filter工作原理(执行流程)
当客户端发出Web资源的请求时,Web服务器根据应用程序配置文件设置的过滤规则进行检查,若客户请求满足过滤规则,则对客户请求/响应进行拦截,对请求头和请求数据进行检查或改动,并依次通过过滤器链,最后把请求/响应交给请求的Web资源处理。请求信息在过滤器链中可以被修改,也可以根据条件让请求不发往资源处理器,并直接向客户机发回一个响应。当资源处理器完成了对资源的处理后,响应信息将逐级逆向返回。同样在这个过程中,用户可以修改响应信息,从而完成一定的任务。
上面说了,当一个请求符合某个过滤器的过滤条件时该请求就会交给这个过滤器去处理。那么当两个过滤器同时过滤一个请求时谁先谁后呢?这就涉及到了过滤链FilterChain。
所有的奥秘都在Filter的FilterChain中。服务器会按照web.xml中过滤器定义的先后循序组装成一条链,然后依次执行其中的doFilter()方法。执行的顺序就如下图所示,执行第一个过滤器的chain.doFilter()之前的代码,第二个过滤器的chain.doFilter()之前的代码,请求的资源,第二个过滤器的chain.doFilter()之后的代码,第一个过滤器的chain.doFilter()之后的代码,最后返回响应。

这里还有一点想补充:大家有没有想过,上面说的“执行请求的资源”究竟是怎么执行的?对于“执行第一个过滤器的chain.doFilter()之前的代码,第二个过滤器的chain.doFilter()之前的代码”这些我可以理解,无非就是按顺序执行一句句的代码,但对于这个“执行请求的资源”我刚开始却是怎么也想不明白。其实是这样的:
通常我们所访问的资源是一个servlet或jsp页面,而jsp其实是一个被封装了的servlet,于是我们就可以统一地认为我们每次访问的都是一个Servlet,而每当我们访访问一个servlet时,web容器都会调用该Servlet的service方法去处理请求。而在service方法又会根据请求方式的不同(Get/Post)去调用相应的doGet()或doPost()方法,实际处理请求的就是这个doGet或doPost方法。写过servlet的朋友都应该知道,我们在doGet(或doPost)方法中是通过response.getWriter()得到客户端的输出流对象,然后用此对象对客户进行响应。
到这里我们就应该理解了过滤器的执行流程了:执行第一个过滤器的chain.doFilter()之前的代码——>第二个过滤器的chain.doFilter()之前的代码——>……——>第n个过滤器的chain.doFilter()之前的代码——>所请求servlet的service()方法中的代码——>所请求servlet的doGet()或doPost()方法中的代码——>第n个过滤器的chain.doFilter()之后的代码——>……——>第二个过滤器的chain.doFilter()之后的代码——>第一个过滤器的chain.doFilter()之后的代码。
过滤器生命周期的四个阶段:
1、实例化:Web容器在部署Web应用程序时对所有过滤器进行实例化。Web容器回调它的无参构造方法。2、初始化:实例化完成之后,马上进行初始化工作。Web容器回调init()方法。
3、过滤:请求路径匹配过滤器的URL映射时。Web容器回调doFilter()方法——主要的工作方法。
4、销毁: Web容器在卸载Web应用程序前,Web容器回调destroy()方法。
Servlet过滤器开发步骤:
1、创建实现javax.servlet.Filter接口的类。
2、过滤器的xml配置。
Servlet过滤器API
Servlet过滤器API包含了3个接口,它们都在javax.servlet包中,分别是Filter接口、FilterChain接口和FilterConfig接口。
public Interface Filter
所有的过滤器都必须实现Filter接口。该接口定义了init,doFilter0,destory()三个方法:
(1) public void init (FilterConfig filterConfig)
当开始使用servlet过滤器服务时,Web容器调用此方法一次,为服务准备过滤器;然后在需要使用过滤器的时候调用doFilter(),传送给此方法的FilterConfig对象,包含servlet过滤器的初始化参数。
(2)public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
每个过滤器都接受当前的请求和响应,且FilterChain过滤器链中的过滤器(应该都是符合条件的)都会被执行。doFilter方 法中,过滤器可以对请求和响应做它想做的一切,通过调用他们的方法收集数据,或者给对象添加新的行为。过滤器通过传送至 此方法的FilterChain参数,调用chain.doFilterO将控制权传送给下一个过滤器。当这个调用返回后,过滤器可以在它的 Filter方法的最后对响应做些其他的工作。如果过滤器想要终止请求的处理或得到对响应的完全控制,则可以不调用下一个过滤 器,而将其重定向至其它一些页面。当链中的最后一个过滤器调用chain.doFilterO方法时,将运行最初请求的Servlet。
(3)public void destroy()
一旦doFilterO方法里的所有线程退出或已超时,容器调用
此方法。服务器调用destoryO以指出过滤器已结束服务,用于释
放过滤器占用的资源。
public interface FilterChain
public void doFilter(ServletRequest request,ServletResponse response)
此方法是由Servlet容器提供给开发者的,用于对资源请求过滤链的依次调用,通过FilterChain调用过滤链中的下一个过滤 器,如果是最后一个过滤器,则下一个就调用目标资源。
public interface FilterConfig
FilterConfig接口检索过滤器名、初始化参数以及活动的Servlet上下文。该接口提供了以下4个方法:
(1)public Java.1ang.String getFilterName0
返回web.xml部署文件中定义的该过滤器的名称。
(2)public ServletContext getServletContextO
返回调用者所处的servlet上下文。
(3)public java.1ang.String getlnitParameter(java.1ang.String name)
返回过滤器初始化参数值的字符串形式,当参数不存在时,返回nul1.name是初始化参数名。
(4)public java.util.Enumeration getlnitParameterNames()
以Enumeration形式返回过滤器所有初始化参数值,如果没有初始化参数,返回为空。
三、应用实例
从上面分析可知,实现Servlet过滤器,需要两步:第一步开发过滤器,设计—个实现Fiker接口的类;第二步通过web.xml配置过滤器,实现过滤器和Servlet、JSP页面之间的映射。以下设计一个简单的IP地址过滤器,根据用户的IP地址进行对网站的访问控制。
(1)过滤器的设计ipfilter.java
-
package ipf;
- imp0rt java.io.IOException;
- imp0rt javax.servlet.*;
-
public class ipfilter implements Filter//实现Filter接口
-
{protected FilterConfig config;
-
protected String rejectedlP;
-
public void init(FilterConfig filterConfig)throws
- ServletException
-
{this.config=filterConfig;//从Web务器获取过滤器配置对象
- rejectedlP=config.getlnitParameter( RejectedlP”):
-
//从配置中取得过滤lP
- }
-
public void doFilter(ServletRequest request,
-
ServletResponse response.FilterChain chain)throws
- IOException,ServletException
-
{RequestDispatcher dispatcher=request.getRequestDispatcher("");
-
String remotelP=request.getRemoteAddrO;//获取客户请求lP
-
int i=remotelP.1astlndexOf(".");
-
int r=rejectedlP.1astlndexOf(”.”):
-
String relPscope=rejectedlP.substring(0,r);//过滤lP段
-
if(relPscope.equals(remotelP.substring(O.i)))
-
{ dispatcher.forward(request,response);//重定向到rejectedError.jsp页面
- retum;//阻塞,直接返Web回客户端
- }
-
else{chain.doFilter(request,response);//调用过滤链上的下一个过滤器
- }
- }
-
public void destroy()
//过滤器功能完成后,由Web服务器调用执行,回收过滤器资源
注意:chain.doFilterO语句以前的代码用于对客户请求的处理;以后的代码用于对响应进行处理。
(2)配置过滤器
在应用程序Web—INF目录下的web.xml描述符文件中添加以下代码:
-
<filter>
-
<filter-name>ipfIter</filter-name>//过滤器名称
-
<filter-class>ipf.ipfiIter</filter-class>//实现过滤器的类
-
<init—param>
-
<param—name>RejectedlP</param-name>//过滤器初始化参数名RejectedlP
-
<param-value>192.168.12.*/param-value>
-
</init—pamm>
-
</filter>
-
<filter-mapping>//过滤器映射(规律规则)
-
<filter-name>ipfiIter</filter-name>
-
<url—pattem>/*</ud-pattem> //规定那些servlet有这个过滤器
- //映射到Web应用根目录下的所有JSP文件
-
</filter-mapping>
通过以上设计与配置,就禁止了IP地址处在192.168.12网段的用户对网站的访问。
4.监听器
1: 监听器的定义:
监听器实际上是一个类,这个类实现了特定的接口,然后将这个类在 web.xml 文件中进行描述,这样服务器在启动的时候就可以实例化这个类,启动监听器。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。例如统计用户在线人数。
web监听器是Servlet规范中定义的一种特殊类,用于监听ServletContext,HttpSession,ServletRequest等域对象的创建、销毁、以及属性的变化等,可以在事件发生前、发生后进行一些处理。
2、监听器的用途
- 1、统计在线人数和在线用户
- 2、系统加载时进行信息的初始化工作
- 3、统计网站的访问量
- 4、跟Spring结合
3、实例
在web.xml文件中进行配置
listenr-class中是自己定义的Listener的类路径
1
2
3
4
5
6
7
8
9
|
public class MyRequestListener implements ServletContextListener{
public void contextInitialized(ServletContextEvent event){
System.out.println("启动监听器");
}
public void contextDestroy(ServletContextEvent event){
System.out.println("结束监听器");
}
}
|
编译器会为我们自动生成contextInitialized和contextDestroy两个函数
servlet加载顺序:context参数配置>监听器>过滤器>serlvet,web.xml中几个配置随意,但是filter中filter-mapping必须在相应filter之后,容器根据filter-mapping的配置顺序创建过滤器,filter配置的时候注意顺序,监听器也是根据配置web中顺序决定,但是注解的方式没法判断顺序
4 监听器的分类
按监听的对象划分,可以分为监听
- ServletContext对象
- HttpSession对象
- ServletRequest对象
按监听的事件划分
- 域对象自身的创建和销毁
- 域对象中属性的创建和消除
- 绑定到session中的某个对象的状态
由于很多监听的方式一致,因此我们只考虑其中的HttpSession对象:
在web.xml中配置session超时
1
2
3
|
<session-config>
<session-timeout> 30 </session-timeout>
</session-config>
|
当超过30秒后,session会自动过期
1
2
3
4
5
6
7
8
9
|
public class MyRequestListener implements HttpSessionListener{
public void sessionCreate(HttpSessionEvent event){
System.out.println("event 创建:");
}
public void sessionDestroy(HttpSessionEvent event){
System.out.println("event 销毁:");
}
}
|
另外,我们可以实现HttpSessionAttributeListener来实现session对象属性的增加(added)、删除(removed)、替换(replaced)的功能
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class MyRequestListener implements HttpSessionAttributeListener{
public void attributeAdded(HttpSessionBindEvent event){
}
public void attributeRemoved(HttpSessionBindEvent event){
}
public void attributeReplaced(HttpSessionBindEvent event){
}
}
|
四、Servlet3.0下监听器的使用
4.2 servlet3.0下监听器的用法
使用注解 @WebListener 即可,无法去定义监听器的顺序
属性名 | 类型 | 是否可选 | 描述 |
value | String | 是 | 该监听器的描述信息 |
@WebListener("This is a listener")
public class FirstListener impliements ServletRequestListener{}
该注解用于将类声明为监听器,被 @WebListener 标注的类必须实现以下至少一个接口:
ServletContextListener
ServletContextAttributeListener
ServletRequestListener
ServletRequestAttributeListener
HttpSessionListener
HttpSessionAttributeListener
五。session的监听器
监听器的分类-按监听事件划分:
1、监听器绑定到HttpSession域中的某个对象的状态事件监听器;
2、HttpSession中的对象状态:
(1)绑定:通过setAttribute();
(2)解除绑定:removeAttribute();
(3)钝化:将session对象序列化到存储设备上
(4)活化:将session对象从存储设备上进行恢复
3、session钝化机制:
(1)把服务器中不常使用的session对象暂时的序列化到系统文件或是数据库中,当使用时反序列化到内存中,整个过程有服务器自动完成。
(2)session的钝化机制由SessionManager管理
4、实现绑定和解除绑定,创建一个【普通的javabean】实现【HttpSessionBindingListener】接口;实现钝化和活化,需实现HttpSessionActivationListener接口和Serializabl e接口,这两个不需要在web.xml中注册