目录
4.3、在javax.servlet和javax.servlet.http包中的各类和接口如下:
1、是什么
Java Servlet 是运行在 Web 服务器或应用服务器上的Java程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎(容器)来控制和调度。针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
2、发展背景
较早在web上传输数据使用的是CGI接口,CGI接口如同一座桥,把网页和服务器中的执行程序连接起来,客户端的指令传递给服务器,服务器执行的结果再返回给HTML页面。但是CGI有一个明显的短板就是每一个请求对应一个进程,当同时有很多请求时,程序挤占系统资源,造成效率低下。
除了CGI接口,当时B/S火爆的时候,同类产品还有:
applet
这是纯客户端(浏览器)方案,applet就是浏览器中的Java插件,浏览器通过它就能够解释执行WEB服务器发过来的Java代码,从而实现动态。但是,显然这种方案不好,既需要浏览器必须安装插件,又受限于浏览器,所以Java代码不能太多和太复杂。
比如,如果安装了JRE,虽然IE浏览器会自动启用Java插件,但是你可以轻易禁止。再比如Chrome还需要你手动去安装插件才行,普通用户连Java是什么都不知道他怎么会去装呢?
Servlet
既然浏览器不方便执行Java代码,那自然还是服务端来执行了,所以Servlet出现了,Servlet就是server端的applet的意思。Servlet倡导了MVC思想。
3、为什么用servlet
- a. 可移植 因为Servlet由Java开发并符合规范定义和广泛接收的API,它可以再不提的操作系统平台和不同的应用服务器平台下移植。
- b. 功能强大 Servlet可以使用Java API核心的所有功能,这些功能包括Web和URL访问、图像处理、数据压缩、多线程、JDBC、RMI和序列化对象等。
- c. 简洁 Servlet代码面向对象,在封装方面具有先天的优势。
- d. 模块化 每一个Servlet可以执行一个特定任务,并且可以讲他们并在一起工作。Servlet之间是可以相互交流的
- e. 扩展性和灵活性 Servlet本身的接口设计得非常精简,使得它有很强的扩展性。需要指出的是,Servlet不等于HttpServlet,后者是前者的一个常见扩展。
- f. 高效耐久 Servlet一旦载入,它就驻留在内存中,这样加快了响应的速度。在服务器上仅有一个Java虚拟机在运行,它的优势在于,当Servlet被客户端发送的第一个请示激活,以后它将继续运行于后台,等待以后的请求。每个请求将生成一个线程而不是进程。单利多线程,性能很是nice
4、具体内容
4.1、Servlet的运行过程
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
②装载并创建该Servlet的一个实例对象。
③调用Servlet实例对象的init()方法。
④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
Servlet调用图
4.2、Servlet生命周期各阶段:
1.实例化:Servlet容器创建Servlet类的实例对象;
2.初始化:容器调用Servlet的init()方法,通常会申请资源以便后续使用;init 方法被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像 Applet 的 init 方法一样。
3.服务:由容器使用以响应客户对Servlet的请求;每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。
4.破坏:在释放Servlet实例前调用,通常会释放资源;
5.不可用:释放内存中的容器;
4.3、在javax.servlet和javax.servlet.http包中的各类和接口如下:
(1)ServletInputStream类
从java.io.InputStream类扩展而来的抽象类,该类创建的对象用于读取客户端请求中的二进制数据,而该类的readLine()方法用于每次读取一行数据;
该方法将从给定偏移处开始的每字节读取到数组中,直到该方法遇到换行符或者读取完一定量的字节数量,该方法返回一个整数来指定实际读取的字节数,到达流尾时返回-1
public int readLine(byte b[],int offset,int length)throws java.io.IOException //b为用于存储读取的数据的字节数组;offset指定方法开始读取字符的起始位置;length读取的最大字节数
(2)ServletOutputStream类
该类创建的对象用于将二进制数据从服务器发送到客户端。具体实现的方法如下:
+ print():将字符串写入客户端。如果发生任何输入或输出异常,则方法print()会引发IOException异常,print()方法接受参数,如char,float,double,int,long,String。
public void print(String str)throws java.io.IOException //str为发送到客户端的字符串
+ println():将字符串写入客户端,紧跟后面输出回车。如果发生任何I/O异常,则会引发IOException
(3)ServletRequest接口
使用ServletRequest接口创建对象,用于使客户端请求信息对Servlet可用。创建的对象作为参数传递至Servlet的service()。
该类实现方法如下:
+ getInputStream():返回客户端请求中的二进制数据,并将其保存在getInputStream对象中
public ServletInputStream getInputStream()throws IOException
+ getParameter():用于获取请求消息一起发送的附加信息-----请求参数
public String getParameter(String str)
+ getContentLength():返回客户端发送的请求的实际长度,如果长度未知,则返回-1
public int getContentLength()
+ getServerName():返回请求发至的服务器名称
public String getServerName()
(4)ServletResponse接口
使用该接口创建的对象用于向客户端提供响应。创建的对象作为参数传递至Servlet的service()方法中。该接口实现的方法如下:
+ getOutpouStream():返回一个ServletOutputStream对象,它被用来发送对客户端的响应
public ServletOutputStream getOutputStream()throws IOException
+ getWriter():返回将字符文本发送到客户端的PrintWriter对象
public PrintWriter getWriter()throws IOException
+ setContentLength():允许用户设置将作为响应放的数据的长度
public void setContentLength(int length)
+ getBufferSize():检索实际的以响应客户端的缓存区大小。若没有使用缓冲区则返回0
public int getBufferSize()
+ setBufferSize():设置将发送到客户端的数据的缓冲区的大小
public void setBufferSize(int size)
(5)HttpServletRequest接口
容器在调用Servlet的doGet()或doPost()方法时,会创建一个HttpServletRequest接口的实例和一个HttpServletResponse接口的实例,作为参数传递给doGet和doPost()方法。该接口代表客户请求,它提供了多种获取请求数据的方法,具体继承层次如图:
(6)HttpServletResponse接口
该接口代表返回给客户端的响应;具体继承层次如图:
(7)ServletConfig接口
在初始化过程中,Servlet容器使用ServletConfig接口的对象作为参数来传递Servlet的配置信息。方法如下:
+ getServletName():用于获取Servlet实例名称
public String getServletName()
+ getInitParameter():检索初始化参数的值,如果参数不存在,则getInitParameter()方法返回null
public String getInitParameter(String name) //name为初始化参数的名称字符串
+ getServletContext():返回Servlet用来与其容器交互的ServletContext对象
public ServletContext getServletContext()
(8)ServletContext接口
该接口定义了一组方法,Servlet使用这些方法与容器进行交互并获取信息(如读写文件等)
+ getContext():返回允许Servlet访问服务器上下文的ServletContext类对象
public ServletContext getServletContext(String uripath) //uripath是Web容器上的另外一个Web程序的上下文路径名称字符串
+ getMimeType():返回文件的MIME类型。MIME定义了一种协议,允许用户通过Internet交换非ASCII消息。不同的MIME类型分为"text/html"和"image/gif"
public String getMimeType(String file) //file是文件名称
+ getResource():返回与路径名相对应的资源的URL
public java.net.URL getResource(String path) throws MalFormedURLexception //path是资源对应的路径名称字符串
(9)获取请求中的数据
在Servlet类的请求处理方法中(如doGet(),doPost()方法),要想获得客户端请求中提交的数据,需要使用HttpServletRequest提供的以下方法:
public String getParameter(String name) //获取指定名称的参数值 public String[] getParameterValues(String name) //获取指定名称参数的所有值数组。它适用于一个参数名对应多个值的情况,如页面表单中的复选框,多选列表提交的值 public java.util.Enumeration getParameterNames() //返回一个包含请求消息中的所有参数名的Enumeration对象。通过遍历这个Enumeration对象,就可以获取请求消息中所有的参数名 public java.util.Map getParameterMap() //返回一个保留了请求消息中所有参数名和值的Map对象。Map对象的key是字符串类型的参数名,value是这个参数所对应的Object类型的值数组。
[提示:在此说明如何处理客户端提交给服务器的数据的乱码问题
若客户端以POST方式提交请求,请求消息主体中的参数数据是按HTML页面中指定的编码方式进行编码的,在Servlet类的请求处理方法中需要先调用 HttpServletRequest接口的setCharacterEncoding(String enc)方法对请求消息主体中的数据按参数指定的编码方式进行编码,然后才能使用上述介绍的方法正 确获得参数值
若客户端使用GET方法请求,上述方法无效,此时,最好的解决方案是在URL中不使用中文等非ASCII字符]
(9)重定向和请求分配
1.重定向:HttpServletRequest接口提供的sendRedirect()方法用于生成302响应码和Location响应头从而通知客户端去重新访问Location响应头中指定的URL
public void sendRedirect(String location)throws IOException //其中location参数指定了重定向的URL,它可以使用绝对或相对URL,Servlet会进行转化
2.请求分派:RequestDispatcher接口,(分派器),定义了下面两个方法
public void forward(ServletRequest request,ServletResponse response)throws ServletException,IOException; //forward()用于将请求转发到RequestDispatcher实例封装的资源 public void include(ServletRequest request,ServletResponse response)throws ServletException,IOException; //include()用于将RequestDispatcher实例封装的资源作为当前响应内容的一部分包含进来
RequestDispatcher dispatcher=request.getRequestDispatcher("servlet1"); //参数为当前servlet名称 dispatcher.forward(request,response);
[重定向与请求分派的区别:
请求分派只能将请求转发到同Web应用中的其他组件;而重定向也可以发送到其他Web;
请求分派过程结束后,浏览器内网址不改变;重定向完成后浏览器内原网址变为重定向目标网址;
]
(11)利用请求域属性传递对象数据
HttpServletRequest接口中提供了几个方法来操作请求实例中的存储对象
public void setAttribute(String name,Object obj) //将对象存储进HttpServletRequest实例中 public Object getAttribute(String name) //检索存储在HttpServletRequest实例中的对象 public Enumeration getAttributeNames() //返回HttpServletRequest实例中所有属性名的Enumeration对象 public void removeAtribute(String name) //从指定HttpServletRequest实例中删除指定名称的属性
这种存储在HttpServletRequest中的对象称之为请求域属性,属于同一请求过程的多个处理模块之间可以通过请求域属性来传递对象数据;
//Servlet1.java
package test
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class Servlet1 extends HttpServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
this.doPost(request,response);
}
public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
String str="Job done";
request.setAttribute("string",str);
RequestDispatcher dispatcher=request.getRequestDispatcher("Servlet2");
dispatcher.forward(request,response);
}
}
//Servlet2.java
package test
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class Servlet2 extends HttpServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
this.doPost(request,response);
}
public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
out.println("<html>");
out.println("<head><title>test</title><head>");
out.println("<body>");
//获取名为"string"的请求域属性的值
String str=(String)request.getAttribute("string");
out.println(str);
out.println("</body>");
out.println("</html>");
}
}
4.4、缺省Servlet
如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。
凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。 例如:
1 <servlet>
2 <servlet-name>ServletDemo2</servlet-name>
3 <servlet-class>gacl.servlet.study.ServletDemo2</servlet-class>
4 <load-on-startup>1</load-on-startup>
5 </servlet>
6
7 <!-- 将ServletDemo2配置成缺省Servlet -->
8 <servlet-mapping>
9 <servlet-name>ServletDemo2</servlet-name>
10 <url-pattern>/</url-pattern>
11 </servlet-mapping>
4.5、Servlet的线程安全问题
当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。例如下面的代码:
线程安全问题产生的原因:
1,多个线程在操作共享的数据;
2,操作共享数据的线程代码有多条;
不存在线程安全问题的代码:
1 package gacl.servlet.study; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 public class ServletDemo3 extends HttpServlet { 11 12 13 public void doGet(HttpServletRequest request, HttpServletResponse response) 14 throws ServletException, IOException { 15 16 /** 17 * 当多线程并发访问这个方法里面的代码时,会存在线程安全问题吗 18 * i变量被多个线程并发访问,但是没有线程安全问题,因为i是doGet方法里面的局部变量, 19 * 当有多个线程并发访问doGet方法时,每一个线程里面都有自己的i变量, 20 * 各个线程操作的都是自己的i变量,所以不存在线程安全问题 21 * 多线程并发访问某一个方法的时候,如果在方法内部定义了一些资源(变量,集合等) 22 * 那么每一个线程都有这些东西,所以就不存在线程安全问题了 23 */ 24 int i=1; 25 i++; 26 response.getWriter().write(i); 27 } 28 29 public void doPost(HttpServletRequest request, HttpServletResponse response) 30 throws ServletException, IOException { 31 doGet(request, response); 32 } 33 34 }
存在线程安全问题的代码:
1 package gacl.servlet.study; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 public class ServletDemo3 extends HttpServlet { 11 12 int i=1; 13 public void doGet(HttpServletRequest request, HttpServletResponse response) 14 throws ServletException, IOException { 15 16 i++; 17 try { 18 Thread.sleep(1000*4); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 response.getWriter().write(i+""); 23 } 24 25 public void doPost(HttpServletRequest request, HttpServletResponse response) 26 throws ServletException, IOException { 27 doGet(request, response); 28 } 29 30 }
把i定义成全局变量,当多个线程并发访问变量i时,就会存在线程安全问题了,如下图所示:同时开启两个浏览器模拟并发访问同一个Servlet,本来正常来说,第一个浏览器应该看到2,而第二个浏览器应该看到3的,结果两个浏览器都看到了3,这就不正常。
线程安全问题只存在多个线程并发操作同一个资源的情况下,所以在编写Servlet的时候,如果并发访问某一个资源(变量,集合等),就会存在线程安全问题,那么该如何解决这个问题呢?
先看看下面的代码:
1 package gacl.servlet.study; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 11 public class ServletDemo3 extends HttpServlet { 12 13 int i=1; 14 public void doGet(HttpServletRequest request, HttpServletResponse response) 15 throws ServletException, IOException { 16 /** 17 * 加了synchronized后,并发访问i时就不存在线程安全问题了, 18 * 为什么加了synchronized后就没有线程安全问题了呢? 19 * 假如现在有一个线程访问Servlet对象,那么它就先拿到了Servlet对象的那把锁 20 * 等到它执行完之后才会把锁还给Servlet对象,由于是它先拿到了Servlet对象的那把锁, 21 * 所以当有别的线程来访问这个Servlet对象时,由于锁已经被之前的线程拿走了,后面的线程只能排队等候了 22 * 23 */ 24 synchronized (this) {//在java中,每一个对象都有一把锁,这里的this指的就是Servlet对象 25 i++; 26 try { 27 Thread.sleep(1000*4); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 response.getWriter().write(i+""); 32 } 33 34 } 35 36 public void doPost(HttpServletRequest request, HttpServletResponse response) 37 throws ServletException, IOException { 38 doGet(request, response); 39 } 40 41 }
现在这种做法是给Servlet对象加了一把锁,保证任何时候都只有一个线程在访问该Servlet对象里面的资源,这样就不存在线程安全问题了,如下图所示:
这种做法虽然解决了线程安全问题,但是编写Servlet却万万不能用这种方式处理线程安全问题,假如有9999个人同时访问这个Servlet,那么这9999个人必须按先后顺序排队轮流访问。
针对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(过时的)。
所以上面的都是废话,为了了解servlet解决多线程问题的历史,这样使用新的解决方案更加游刃有余。
Servlet默认是多线程模式执行的,当有多个用户同时并发请求一个Servlet时,容器将启动多个线程调用相应的请求处理方法,此时,请求处理方法中的局部变量是安全的,但对于成员变量和共享数据是不安全的,因此这多个线程有可能同时都操作这些数据,这是需要同步处理。所以,编写代码时需要非常细致的考虑多线程的安全性问题。多数人编写Servlet时不注意多线程问题,导致少量用户访问时没有问题,但并发量大则出现莫名其妙的问题。
解决:
1.使用synchronized:使用synchonized关键字同步操作成员变量和共享数据的代码,就可以防止出现线程安全性问题,但这也意味着线程需要排队处理。因此,在使用同步语句时要尽可能缩小同步代码范围,不能直接在请求处理方法(如doGet(),doPost()方法)使用同步,这样会严重影响效率。
2.尽量少使用成员变量和共享数据:对于集合,使用Vector代替非线程安全的ArrayList,使用Hashtable代替HashMap;不能在Servlet内创建自己的线程,导致复杂化。
因此,对jsp可能用到的变量要注意:
实例变量: 实例变量是在堆中分配的,并被属于该实例的所有线程共享,所以不是线程安全的。
JSP系统提供的8个类变量
JSP中用到的OUT,REQUEST,RESPONSE,SESSION,CONFIG,PAGE,PAGECONXT是线程安全的(因为每个线程对应的request,respone对象都是不一样的,不存在共享问题), APPLICATION在整个系统内被使用,所以不是线程安全的。
局部变量: 局部变量在堆栈中分配,因为每个线程都有它自己的堆栈空间,所以是线程安全的.
静态类: 静态类不用被实例化,就可直接使用,也不是线程安全的.
外部资源: 在程序中可能会有多个线程或进程同时操作同一个资源(如:多个线程或进程同时对一个文件进行写操作).此时也要注意同步问题.
4.6、servlet过滤器
是什么?
Servlet过滤器是 Servlet 程序的一种特殊用法,主要用来完成一些通用的操作,如编码的过滤、判断用户的登录状态。过滤器使得Servlet开发者能够在客户端请求到达 Servlet资源之前被截获,在处理之后再发送给被请求的Servlet资源,并且还可以截获响应,修改之后再发送给用户。而Servlet监听器可以 监听客户端发出的请求、服务器端的操作,通过监听器,可以自动激发一些操作,如监听在线人数。
它只提供过滤作用,Servlet过滤器能够在Servlet程序(JSP页面)被调用之前检查 request对象,修改请求头和请求内容,在Servlet程序(JSP页面)被调用之后,检查response对象,修改响应头和响应内容。
Servlet过滤器的特点
1.Servlet过滤器可以检查和修改request和response对象。
2.Servlet过滤器可以被指定与特定的URL关联,只有当客户请求访问该特定的URL时,才会触发过滤器。
3.Servlet过滤器可以被串联成串,形成过滤链,协同修改请求和响应。
Servlet过滤器的作用如下:
1.查询请求并作出相应的行动。
2.阻塞请求--响应对,使其不能进一步传递。
3.修改请求头和内容,用户可以提供自定义的请求。
4.修改响应头和内容,用户可以通过提供定制的响应版本实现。
5.与外部资源进行交互。
创建一个Servlet过滤器需要下面的步骤:
1.创建一个实现了javax.servlet.Filter接口的类。
2.重写init(FilterConfig)方法,读入为过滤器配置的初始化参数,申请过滤器需要的资源。
3.重写方法doFilter(ServletRequest,ServletResponse,FilterChain),完成过滤操作,可以 从ServletRequest参数中得到全部的请求信息,从ServletResponse参数中得到全部的响应信息。
4.在doFilter()方法的最后,使用FilterChain参数的doFilter()方法将请求和响应后传。形成过滤器链
5.对响应的Servlet程序和JSP页面注册过滤器,在部署描述文件(web.xml)中使用<filter-apping>和<filter>元素对过滤器进行配置。
特点
Filter 先于 Servlet 存在于服务端 在应用中 允许存在多个 Filter ,到底哪一个 Filter 先执行哪一个后执行,这 取决于在 web.xml 中定义的先后次序 (如果使用注解配置,则 Filter 的执行顺序由 Filter 的类名的字母的顺序 来决定,如 AFilter 和 BFilter,则先执行 AFilter) 一个 Filter 可以配置多个 也可以对 指定的 Servlet 做过滤(注解通过 servletNames 指定,配置由 指定) 默认情况下,Filter 只对新的请求做拦截,如果是请求转发,则不会过滤。也可以通过设置来修改。。。
一个 Filter 可以配置多个 也可以对指定的 Servlet 做过滤(注解通过 servletNames 指定,配置由 指定)
默认情况下,Filter 只对新的请求做拦截,如果是请求转发,则不会过滤。
配置项指定了 Filter 的过滤时间:
REQUEST:只对请求做过滤,默认选项,如果有该配置项则必须显式写明
FORWARD:只对请求转发(forword)方式做过滤
ERROR:只对跳转到全局的错误页面做过滤
INCLUDE:只对请求包含(include)方式做过滤
对应的注解属性为:dispatcherTypes
4.7、servlet监听器
Servlet监听器也叫做 listener,通过它可以监听Web应用的上下文(环境)信息、Servlet请求信息、Servlet会话信息,并自动根据不同情况,在后台调用相 应的处理程序。通过监听器,可以自动激发一些操作,比如监听在线人数,当增加一个HttpSession时就激发 sessionCreated(HttpSessionEvent)方法,这样就可以给在线人数加1。
监听对象 | 监听接口 | 监听事件 |
ServletRequest | ServletRequestListener (2个方法) | ServletRequestEvent |
ServletRequestAttributeListener (3个方法) | ServletRequestAttributeEvent | |
HttpSession | HttpSessionListener (2个方法) | HttpSessionEvent |
HttpSessionActivationListener (2个方法) | ||
HttpSessionAttributeListener (3个方法) | HttpSessionBindingEvent | |
HttpSessionBindingListener (2个方法) | ||
ServletContext | ServletContextListener (2个方法) | ServletContextEvent |
ServletContextAttributeListener (3个方法) | ServletContextAttributeEvent |
web.xml的加载顺序是:context-param->listener->filter->servlet
区别
1,servlet 流程是短的,url传来之后,就对其进行处理,之后返回或转向到某一自己指定的页面。它主要用来在 业务处理之前进行控制.
2,filter 流程是线性的, url传来之后,检查之后,可保持原来的流程继续向下执行,被下一个filter, servlet接收等,而servlet 处理之后,不会继续向下传递。filter功能可用来保持流程继续按照原来的方式进行下去,或者主导流程,而servlet的功能主要用来主导流程。
filter可用来进行字符编码的过滤,检测用户是否登陆的过滤,禁止页面缓存等
3, servlet,filter都是针对url之类的,而listener是针对对象的操作的,如session的创建,session.setAttribute的发生,在这样的事件发生时做一些事情。
可用来进行:Spring整合Struts,为Struts的action注入属性,web应用定时任务的实现,在线人数的统计等;
4,interceptor拦截器,类似于filter,不过在struts中配置,不是在web.xml,并且不是针对url的而是针对action的,当页面提交时,进行过滤操作;
执行流程
1.servlet:
2.filter
3.listener
4.interceptor
4.8、servlet文件上传下载
具体的操作可以观看https://www.cnblogs.com/xdp-gacl/p/4200090.html