Web组件:Servlet

来源:互联网 发布:美利坚大学 知乎 编辑:程序博客网 时间:2024/05/26 12:57

定义

一种用于java web项目的多线程、单例模式的组件,由web容器(如tomcat)进行管理。


生命周期

Servlet的生命周期大概可以分为6个阶段:

1. 客户端请求servlet。

2. servlet此时被装载到内存中servlet在web容器启动时就被装载到内存中(下文的<load-on-startup>)。

3. servlet被实例化。

4. 初始化各项参数:init()。

5. service():doGet()或doPost()。

6. 销毁:destroy()。


项目目录结构

以tomcat容器为例,项目存放在:/tomcat/webapps/项目名
每个项目根目录下包含:
index.html / index.htm / index.jsp
html,css,js,jsp,自定义文件夹等
[WEB-INF]
web.xml
[lib]
[classes]
Servlet.class文件

配置web.xml

servlet标签

对于每个Servlet,我们需要一个Servlet标签来将其在web.xml上用<servlet>标签“登记”。
每个<servlet>至少包含以下两个子标签:
<servlet-name>:servlet引用名,用于web.xml内部进行引用
<servlet-class><jsp-file>:servlet类路径或jsp文件路径
注:子标签需按上述顺序排列。
例:
<servlet>    <servlet-name>MyServlet</servlet-name>    <servlet-class>com.servlet.MyServlet</servlet-class></servlet>
除了上述两个必要子标签之外,<servlet>还有许多其他有用的子标签。
<init-param>:定义servlet装载时的初始参数,以键值对的形式配置,可在servlet中取用
<init-param>    <param-name>123</param-name>    <param-value>456</param-value></init-param>
<load-on-startup>:定义是否在web容器启动时就装载这个servlet,默认情况下或配置为负数时,只有当有请求时才装载servlet;当配置为非负数时,web容器在启动的过程中就装载该servlet,数字越小优先级越高。

Servlet-mapping标签

这个标签用于对servlet-name与访问URL进行映射,包含两个子标签,同样需要按顺序排列:
<url-pattern>:URL格式,匹配这个格式的访问URL将跳转到servlet-name对应的servlet中进行处理
<servlet-name>:在<servlet>子标签<servlet-name>中配置的引用名
对于<url-pattern>,一共有四类匹配格式:
1. 精确匹配
<servlet-mapping><url-pattern>/servlet/myServlet</url-pattern><servlet-name>MyServlet</servlet-name></servlet-mapping>
访问:http://IP地址或域名地址/项目名/servlet/myServlet

2. 扩展名匹配
<servlet-mapping><url-pattern>*.html</url-pattern><servlet-name>MyServlet</servlet-name></servlet-mapping>
访问:http://IP地址或域名地址/项目名/任意字符或空.html

3. 路径映射
<servlet-mapping><url-pattern>/servlet/*</url-pattern><servlet-name>MyServlet</servlet-name></servlet-mapping>
访问:http://IP地址或域名地址/项目名/任意字符或没有

4.默认匹配
<servlet-mapping><url-pattern>/*</url-pattern><servlet-name>MyServlet</servlet-name></servlet-mapping>
访问:http://IP地址或域名地址/项目名/

<welcome-file-list>标签

用于配置欢迎页面,包含至少一个<welcome-file>子标签。当访问“http://IP地址或域名地址/项目名/”时显示。
<welcome-file-list>    <welcome-file>index.html</welcome-file>    <welcome-file>index.htm</welcome-file>    <welcome-file>index.jsp</welcome-file>    <welcome-file>default.html</welcome-file>    <welcome-file>default.htm</welcome-file>    <welcome-file>default.jsp</welcome-file></welcome-file-list>
注:在WebContent文件夹中至少需要有一个欢迎页面,否则访问“http://IP地址或域名地址/项目名/”时将返回404错误。

<error-page>标签

配置错误页面,一个错误代码对应一个页面。
<error-page><error-code>404</error-code><location>/errorpage/404.jsp</location></error-page>

数据传递对象

请求/响应对象

分别是service()方法里的request对象和response对象,它们都是“一次性”变量。

请求对象

一个request包含请求头+请求体,请求头包含各种操作参数,请求体则包括需要处理的数据。

Attribute

public String getAttribute(String attrName);
public Enumeration getAttributeNames();

public void removeAttribute(String attrName);

public void setAttribute(String attrName, Object attrValue);

获取指定名称的attribute,获取所有attribute名称,移除和设置指定名称的attribute,attribute在具有转发关系的request之间共享。与Parameter用于从web客户端(如浏览器的form提交)向web服务端传递数据不同,Attribute是一种web容器内部传递数据的对象。


字符编码

public String getCharacterEncoding();

public void setCharacterEncoding(String encoding);

获取和设置消息体的字符编码。


MIME

public int getContentLength();

public String getContentType();

获取MIME的长度和类型,若MIME信息没有被填充,长度返回-1,类型返回null。


路径

public String getContextPath();

public String getRequestURI();

public String getRequestURL();

public String getServletPath();

public String getPathInfo();

分别是获取上下文、URI、URL、Servlet根目录、Servlet子路径,以一个实例来说明。

web项目名=myproject,请求的servlet(或jsp)=MyServlet,访问http://IP地址或域名地址/myproject/MyServlet,那么:

getContextPath() = /myproject

getRequestURI() = /myproject/MyServlet

getRequestURL() = http://localhost/myproject/MyServlet

getServletPath() = /MyServlet

如果请求的是jsp,有可能有多级目录,如/jsp/my.jsp,那么getRequestURI()和getServletPath()的返回值会相应地变化。

web.xml配置中使用通配符时:

<servlet-mapping>  <servlet-name>MyServlet</servlet-name>  <url-pattern>/*</url-pattern></servlet-mapping>

当访问http://IP地址或域名地址/myproject/anyPath,getPathInfo()将返回/anyPath。

当对于同一个servlet的映射既配置了通配又配置了精确匹配的时候,访问http://IP地址或域名地址/myproject/MyServlet则返回null。

getPathInfo的返回值不会包含Parameter。


Cookie

public Cookie[] getCookies();

获取请求消息发来的Cookie数组。


请求头

public String getMethod();

public long getDateHeader(String headerName);

public String getHeader(String headerName);

public Enumeration getHeaderNames();

public Enumeration getHeaders(String headerName);

public int getIntHeader(String headerName);

public String getProtocol();

public String getScheme();

以上几个API均为直接或间接地解析请求消息头。消息头是由若干对键值对组成的。getMethod获取消息请求得方式,GET或POST;getHeader获取指定名称的消息头的值,getDateHeader及getIntHeader对Date型和Int型的头部值做了进一步的类型转换,而getHeaderNames则获取消息头部名的枚举;

两个简单的例子:

response.setHeader("Refresh","3;url=NewServlet"); //三秒后跳转至NewServlet

response.addHeader("Content-Disposition","attachment;filename="+fileName); //设置下载文件时的文件名

getProtocol获取通信协议,通常为HTTP/1.1,格式:protocol/majorVersion.minorVersion。

getScheme获取访问方式,比如http、https、ftp等。


本机信息

public String getLocalAddr();

public String getLocalName();

public String getLocalPort();

public String getServerName();

public int getServerPort();

获取本机IP地址、域名及web服务的端口。后两个API效果似乎跟第二三个效果一样。


Locale信息

public Locale getLocale();

public Enumeration getLocales();

获取客户端的Locale信息,用于国际化开发。


Parameter

public String getParameter(String paramName);

public Map getParameterMap();

public Enumeration getParameterNames();

public String[] getParameterValues();

public String getQueryString();

获取Parameter的各种API,getQueryString是获取GET方法传入的整个Parameter键值对字符串,以“?”起始,以“&”分隔,比如访问http://IP地址或域名地址/myproject/MyServle?p1=v1则返回p1=v1。


请求体

public BufferedReader getReader();
利用返回的BufferedReader对象可以对消息体进行输入和解析。

远端信息

public String getRemoteAddr();

public String getRemoteHost();

public int getRemotePort();

获取对端(客户端)的IP地址、域名地址以及端口。


请求派发

public RequestDispatcher getRequestDispatcher(String arg0);

获取请求派发器,下文将说明。


Session

public HttpSession getSession();

public HttpSession getSession(boolean createNew);
都是获取客户端的Session信息,区别在于第二个方法的参数为true时,与第一个方法相同,若已经存在session则返回该session,若没有session则立即创建一个新的session;为false时,若已经存在session则返回session,若没有session则返回null。


响应对象

Cookie

public void addCookie(Cookie);

为响应消息添加一个Cookie。


响应头

public void addDateHeader(String headerName, Date headerValue);

public void addHeader(String headerName, String headerValue);

public void addIntHeader(String headerName, int headerValue);

public boolean containsHeader(String headerName);

public void setDateHeader(String headerName, Date headerValue);
public void setHeader(String headerName, String headerValue);
public void setIntHeader(String headerName, int headerValue);

操作响应消息头的键值对和检查是否包含指定名称的消息头。


字符编码

public void encodeRedirectURL(String encode);

public void encodeURL(String encode);

public String getCharacterEncoding();

public void setCharacterEncoding(String encode);

分别是对重定向的URL、跳转的URL以及字符的编码进行操作。


缓冲

public void flushBuffer();

public int getBufferSize();

public void resetBuffer();

public void setBufferSize(int bufferSize);

对输出缓冲的操作,未深入研究。


MIME

public String getContentType();

public void setContentLength(int length);

public void setContentType(String type);

对MIME信息进行设置,对端(客户端)解析MIME信息后可以调用相应的应用来处理返回的响应消息。

常见的Content-Type:text/html(默认),image/jpeg(图片),application/vnd.ms-excel(Excel文件),application/x-msdownload(下载)。


Locale

public Locale getLocale();

public void setLocale(Locale locale);
对Locale信息的处理,用于国际化开发。

输出对象

public ServletOutputStream getOutputStream();

public PrintWriter getWriter();

根据输出方式的不同选择输出对象,ServletOutputStream或PrintWriter对象,作用和使用类似,为了避免冲突不应同时使用。

ServletOutputStream多用于传输文件(字节流),PrintWriter传输文本。


错误/状态信息

public void sendError(int errorCode);

public void sendError(int errorCode, String errorMsg);

public void setStatus(int status);

跳转到错误页面或者设置状态。

sendError可以根据web.xml中<error-page>标签的配置跳转到自定义的错误提示页面。

setStatus也有类似的功能,但是不能根据<error-page>配置进行跳转,而是有一套定义好的状态码。

简单来说就是sendError可以跳转到自定义页面,setStatus的参数除了包含错误状态外,还包含了其它许多状态,比如:

100-199:请求正在进行
200-299:请求成功
300-399:表示用于已移走的文件,指示新地址
400-499:客户端错误
500-599:服务端错误

重定向

public void sendRedirect(String redirectedURL);

指定重定向的URL,下文将详细说明。


上下文对象

ServletContext实例被所有Servlet共享,显然是单例模式,作用范围是单个的web应用,常用于存放全局变量、读取资源文件。

获取
ServletContext ctx = this.getServletContext();

Attribute
public Object getAttribute(String attrName);
public Enumeration getAttributeNames();
public void removeAttribute(String attrName);
public void setAttribute(String attrName, Object attrValue);
有点类似于request对象控制的attribute,区别在于作用于是整个web应用,这也是ServletContext实例的核心作用。

路径/服务器信息
public String getContextPath();
public String getRealPath(String subpath);
public String getServerInfo();
public String getServletContextName();
getContextPath这个方法request对象里也有,已经提过不再赘述。
getRealPath返回web应用的真实路径(硬盘绝对路径)+指定的subpath字符串,如ctx.getRealPath("\\");返回“真实路径\”。
getServerInfo返回web容器的信息,如Apache Tomcat/7.0.20
getServletContextName返回Servlet所在的Context名,一般是web应用的名称(项目名)。

初始化参数
public String getInitParameter(String paramName);
public Enumeration getInitParameterNames();
这些初始化参数配置在web.xml中,与<servlet>标签同级,作用域:web应用全局。
<context-param>  <param-name>pName</param-name>  <param-value>pValue</param-value></context-param>

版本信息
public int getMajorVersion();
public int getMinorVersion();
如果使用HTTP/1.1,则两者均返回1。

请求转发器
public RequestDispatcher getNamedDispatcher(String dispatcherName);
public RequestDispatcher getRequestDispatcher(String dispatcherName);
getNamedDispatcher根据Servlet的名称来获取指定Servlet的请求转发器,从而进行转发,格式如MyServlet。
getRequestDispatcher根据Servlet的“绝对路径”(相对于getNamedDispatcher),格式如/MyServlet(配置于web.xml中的<url-pattern>标签)或/myproject/my.jsp,需要以“/”开头。

资源
public URL getResource(String resName);
public InputStream getResourceAsStream(String resName);
public Set getResourcePaths(String resName);
获取资源,返回URL对象、输入流对象,以及返回包含所有资源路径的Set对象。
注意:getResource和getResourceAsStream的参数为以Web根目录(如tomcat的webContent)为起始的相对路径,且必须以“/开头”,如:
String resName = "/WEB-INF/html/x.html";

参数对象

ServletConfig对象为Servlet提供初始化参数,作用域为单个Servlet。
获取
ServletConfig config = this.getServletConfig();

初始化参数
public String getInitParameter(String paramName);
public Enumeration getInitParameterNames();
这些参数配置在<servlet>标签里。

Servlet信息
public String getServletContext();
public String getServletName();
这两个在上文都有描述,不再赘述。

请求派发与重定向

先说重定向,它其实有两次HTTP请求,地址也产生了变化,浏览器(客户端)也参与了重定向过程,Servlet处理的request和response对象也发生了变化。
而请求派发只有一次HTTP请求,发生在同一个web服务器里,request和response对象不变。
简单来说,如果把客户端看成一个买家,服务端看成卖家,重定向就是买家向卖家A购买商品,A没库存了,就把卖家B介绍给买家,买家再向卖家B购买商品,之后的交易买家A不再参与;而请求派发就是买家同样向卖家A购买商品,A再从卖家B处进货,然后再卖给买家,买家没有跟卖家B直接接触。

重定向

重定向的API很简单,只有一句:
response.sendRedirect(newURL);
注:需要完整的重定向目标URL参数。
例子:请求ServletA,重定向至ServletB。
//ServletA的doGet方法protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("This is Servlet A");response.sendRedirect(request.getContextPath()+"/ServletB");return;}
</pre><pre name="code" class="java">//ServletB的doGet方法protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("This is Servlet B");}
输出:
This is Servlet AThis is Servlet B

请求派发

两个API分别是ServletContext对象的getRequestDispatcher和getNamedDispatcher,例:
//ServletA的doGet方法,使用getNamedDispatcher,需要指定Servlet名称protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {RequestDispatcher dispatcher = getServletContext().getNamedDispatcher("ServletB");dispatcher.forward(request, response);}
//ServletA的doGet方法,使用getRequestDispatcher,需要指定Servlet的url-patternprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/ServletB");dispatcher.forward(request, response);}

传递Attribute
获取目标Servlet的请求派发器后,可以使用Attribute在具有转发关系的Servlet之间传递数据,例:
//ServletA的doGet方法protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setAttribute("attr1", "value1");RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/ServletB");dispatcher.forward(request, response);}
//ServletB的doGet方法protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String attr1 = (String) request.getAttribute("attr1");}

forward与include
forward方法在原Servlet将请求派发出去之后移交了控制权,响应交由被派发的Servlet进行处理,等被派发的Servlet处理完后直接响应,可能覆盖原Servlet对响应的处理;include方法则在派发的Servlet处理完之后继续进行处理,与之前的处理进行合并等等,然后再进行响应。
//ServletB的doGet方法protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.getWriter().println("<html><body>This is ServletB</body></html>");}

forward派发

//ServletA的doGet方法, forward方式派发protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.getWriter().println("<html><body>This is ServletA</body></html>"); //将被被派发的Servlet对响应消息的处理覆盖RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/ServletB");dispatcher.forward(request, response);}
输出页面源码:
<html><body>This is ServletB</body></html>
include派发
//ServletA的doGet方法, include方式派发protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.getWriter().println("<html><body>This is ServletA</body></html>");RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/ServletB");dispatcher.include(request, response);}
输出页面源码:
<html><body>This is ServletA</body></html><html><body>This is ServletB</body></html>

客户端状态保持

由于HTTP协议是无状态协议,因此若要达到保持客户端状态的目的,需要添加解决方案,如Cookie,Session,URL重写等。

Cookie

每个Cookie都是一个单一的键值对,用于将服务端的一部分数据存储于客户端。一旦产生,Cookie将被添加到每一个响应头部,告知浏览器(客户端)进行存储。

生成Cookie
Cookie cookie = new Cookie("name", "value");

有效期
public int getMaxAge();
public void setMaxAge(int expiry);
以秒为单位的有效期,默认为负数(-1),表示Cookie失效;设置为正数时生效;设置为0时将从客户端删除这个Cookie。

键值对
public String getName();
public void setName(String name);
public String getValue();
public void setValue();
对Cookie包含的唯一一对键值对进行操作。

发送Cookie
response.addCookie(cookie);
在响应对象中添加Cookie。

获取Cookie
Cookie[] cookies = request.getCookies();
在请求对象中获取Cookie。

Session

Session其实是一种特殊的快速Cookie,除了Cookie的特性外,它还有一个Session ID,用于区分不同的Session。一般来说,每个浏览器进程与服务端的交互共用一个Session( 未超时的情况下),也有特殊情况(每个页面tab一个Session)。

生命周期
Session创建于浏览器(客户端)第一次发出请求的时候,然而客户端和服务端都可以结束它。当浏览器进程结束时,客户端结束Session;当客户端超时未再次请求时,服务端对Session进行清理。

结束
一共有三种结束Session的时机:
1. 超时,<session-config>的<session-timeout>子标签,单位为分钟;<session-config>与<servlet>标签同级,配置于web.xml中。
<session-config><session-timeout>15</session-timeout></session-config>
2. 手动结束。
HttpSession session = request.getSession();session.invalidate();
3. web应用程序崩溃。

获取
HttpSession session = request.getSession(); //如有取得,如无则生成并返回HttpSession session = request.getSession(false); //如有取得,如无返回null
上文中request的API中已说明,不再赘述。

Attribute
public Object getAttribute(String attrName);public Enumeration getAttributeNames();public void removeAttribute(String attrName);public void setAttribute(String attrName, Object attrValue);
Session同样有Attribute,作用范围显然就在当前Session的生命周期内。

Session信息
public long getCreationTime();
public long getLastAccessedTime();
public String getId();
public boolean isNew();

获取session的创建时间,最近一次使用时间,ID,以及是否为新创建的session。


有效期

public int getMaxInactiveInterval();public void invalidate();public void setMaxInactiveInterval(int maxInt);
获取最大有效期、手动让session失效、设置最大有效期,默认的MaxInactiveInterval是1800s。设置为-1时为永不会超时过期。

其他

主要包括URL重写以及隐藏字段(<input type='hidden'/>)传递给request等,都是当Cookie被禁用时的迂回手段,就不多说了。

监听器

监听器(Listener)能够捕捉Servlet运行过程中的各种事件(Event),在事件的前后可以进行各种操作以满足需求。事件在诸多接口中进行处理:
Servlet Context的事件
1. 监听生命周期,ServletContextListener
2. 监听Attributes改变,ServletContextAttributeListener

HTTP Session的事件
1. 监听生命周期,HttpSessionListener
2. 监听Attributes改变,HttpSessionAttributeListener
3. 监听Session变动,HttpSessionActivationListener
4. 监听Session对象绑定,HttpSessionBindingListener

Servlet Request的事件
1. 监听生命周期,ServletRequestListener
2. 监听Attributes改变,ServletRequestAttributeListener

web.xml

第一步总得在web.xml中进行配置。Listener的配置比较简单,只需要一个<listener>标签:
<listener>    <listener-class>com.listener.MyListener</listener-class></listener>

Context事件

生命周期
public void contextInitialized(ServletContextEvent arg0);
public void contextDestroyed(ServletContextEvent arg0);
两个方法中的事件对象arg0用法一样,即都能获取Context对象。
ServletContext ctx = arg0.getServletContext();

Attribute改变
public void attributeAdded(ServletContextAttributeEvent arg0);
public void attributeReplaced(ServletContextAttributeEvent arg0);
public void attributeRemoved(ServletContextAttributeEvent arg0);
这个事件对象可以获取到被添加、替换、删除的Attribute的名字和值。
String name = arg0.getName();Object value = arg0.getValue();
注:在attributeReplaced方法中,arg0.getValue()获取的是被替换的值,用于替换的值需要:
arg0.getServletContext().getAttribute(arg0.getName());

Session事件

生命周期
public void sessionCreated(HttpSessionEvent arg0);
public void sessionDestroyed(HttpSessionEvent arg0);
触发的时机分别是Session创建和销毁,arg0都能获取Session对象。

Attribute改变
public void attributeRemoved(HttpSessionBindingEvent arg0);
public void attributeAdded(HttpSessionBindingEvent arg0);
public void attributeReplaced(HttpSessionBindingEvent arg0);
与Context的Attribute改变事件类似,只不过这里监听的是Session的Attribute。

Session变动
public void sessionDidActivate(HttpSessionEvent arg0);
public void sessionWillPassivate(HttpSessionEvent arg0);
这两个事件通常在web应用部署在分布式环境下时才会触发,即当Session从一个JVM转移到另一个JVM时才会触发。转移Session的目的常为负载均衡或冗余备份。

对象绑定
public void valueUnbound(HttpSessionBindingEvent arg0);
public void valueBound(HttpSessionBindingEvent arg0);
当预定义对象绑定到Session及解除与Session的绑定时触发,这里的绑定与解绑同Attribute改变有微妙的联系。valueUnbound触发时一定会触发attributeRemoved,反之不一定;valueBound触发时一定会触发attributeAdded,反之也不一定。更进一步说明,只有当实现了HttpSessionBindingListener接口的Attribute对象绑定及解绑时才会触发valueBound和valueUnbound,例:
public class SpecialAttribute implements HttpSessionBindingListener{    ...}
首先创建一个SpecialAttribute实例:
SpecialAttribute sAttr = new SpecialAttribute();
接着将其绑定至Session对象,与通常的Attribute无异:
session.setAttribute("sAttr", sAttr);session.removeAttribute("sAttr");
只有当以上两句执行时,才会触发valueBound和valueUnbound,普通的Attribute改变只会触发Attribute改变事件。

Request事件

生命周期
public void requestDestroyed(ServletRequestEvent arg0);
public void requestInitialized(ServletRequestEvent arg0);
在request创建和销毁时触发,arg0获取request对象。当访问频繁时这个事件应该会经常被触发。

Attribute改变
public void attributeAdded(ServletRequestAttributeEvent arg0);
public void attributeRemoved(ServletRequestAttributeEvent arg0);
public void attributeReplaced(ServletRequestAttributeEvent arg0);
与Context和Session的Attribute改变事件类似,不过此处监听的是request的Attribute。

过滤器

顾名思义,Servlet的过滤器就是在Servlet执行某些操作的前后对请求进行预处理,其生命周期与Servlet的生命周期相关。
具体来说,过滤器处理的request对象,对请求消息的请求头或请求体进行处理。
同一个Servlet可以应用多个过滤器,这些过滤器依照顺序形成一条过滤器链,每个请求需要经过这些链,层层关卡之后才到达Servlet手中。

web.xml

单个的Filter配置和Servlet配置类似,与<servlet>标签同级,首先将Filter的名字和类对应起来,接着配置哪些url-pattern或哪些Servlet要应用该Filter,如:
针对某个URL规则:
<filter>    <filter-name>MyFilter</filter-name>    <filter-class>com.filter.MyFilter</filter-class></filter><filter-mapping>    <filter-name>MyFilter</filter-name>    <url-pattern>/MyURL</url-pattern></filter-mapping>
针对某个Servlet:
<filter>    <filter-name>MyFilter</filter-name>    <filter-class>com.filter.MyFilter</filter-class></filter><filter-mapping>    <filter-name>MyFilter</filter-name>    <servlet-name>MyServlet</servlet-name></filter-mapping>
还有其他规则,这套URL匹配的规则跟Servlet是一样的。
匹配所有URI:
<filter-mapping><filter-name>MyFilter</filter-name><!-- 过滤匹配选项 --><!-- 匹配所有 --><url-pattern>/*</url-pattern></filter-mapping>
匹配所有jsp:
<filter-mapping><filter-name>MyFilter</filter-name><!-- 匹配所有jsp --><url-pattern>*.jsp</url-pattern></filter-mapping>
匹配所有html:
<filter-mapping><filter-name>MyFilter</filter-name><!-- 匹配所有html --><url-pattern>*.html</url-pattern></filter-mapping>
注:如用一个Filter匹配多个规则,则需要多个<filter-mapping>定义不同规则映射到同一个<filter-name>上,一个<filter-mapping>对应一个过滤规则。
与Servlet类似,Filter也可以添加初始化参数:
<filter><filter-name>MyFilter</filter-name><init-param><param-name>MyParam</param-name><param-value>MyParamValue</param-value></init-param><filter-class>com.filter.MyFilter</filter-class></filter>

Filter接口

主要有三个方法:
public void init(FilterConfig fConfig);
public void destroy();
public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain);
init主要利用FilterConfig对象读取Filter初始化的参数,包括:
public String getFilterName();
public String getInitParameter(String paramName);
public Enumeration getInitParameterNames();
public ServletContext getServletContext();
destroy没什么说的,Filter生命周期结束时调用。
起过滤作用的是doFilter方法,此方法由容器在有请求时根据web.xml的配置调用,它能获取到request和response对象,这两个对象的操作在前文已经描述过。
当匹配的过滤器只有一个时,
当匹配的过滤器不止一个时,所有匹配的过滤器形成一条过滤器链;每当过滤器处理完请求和响应对象后,需要将这一对请求响应对象往过滤链上的下一个过滤器传递;如果已经到链尾,将传递给Servlet。
传递的方法:
chain.doFilter(request, response);

过滤链排序
当匹配的过滤器数目不止一个时,所有匹配的过滤器按照如下规则进行排序:
1. 第一批次,根据包含<url-pattern>子标签的<filter-mapping>标签在web.xml中的先后顺序排序。
2. 第二批次,根据包含<servlet-name>子标签的<filter-mapping>标签在web.xml中的先后顺序排序。

实例

以过滤器实现编码转换为例。在tomcat容器中,对GET方式提交的请求默认用ISO8859-1字符编码集进行编码(如Parameter)。这在纯英文环境下没有问题,在有外文涉及的时候(如中文)可能出现乱码。
最简单的解决方案,显然是手动在每一个Servlet中对request和response进行字符编码处理,在接收时可以:
String name = new String(request.getParameter("name").getBytes("ISO8859-1"),"UTF-8");
在发送时可以拼接:
String name = URLEncoder.encode("somename","UTF-8");
String path = "http://localhost/myWeb/login?name="+name;
当然,还有上文提到的request对象和response对象的字符编码方法。
这个解决方案的缺点是虽然能保证编码正常,但是需要对每一个Servlet中的request/response进行处理,显然低效且代码较为冗余,这个时候就需要过滤器了。
下面这个实例,我就不多说了,直接放代码。

web.xml
<filter><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param></filter>
*Servlet和filter-mapping略。

EncodingFilter.java
/** * 监听器类 * */public class EncodingFilter implements Filter{<span style="white-space:pre"></span>private HttpServletRequest req;<span style="white-space:pre"></span>private String encoding;<span style="white-space:pre"></span>@Override<span style="white-space:pre"></span>public void destroy() {}<span style="white-space:pre"></span>@Override<span style="white-space:pre"></span>public void doFilter(ServletRequest request, ServletResponse response,FilterChain filterChain) throws IOException, ServletException {<span style="white-space:pre"></span>//请求对象转型<span style="white-space:pre"></span>req = (HttpServletRequest)request;<span style="white-space:pre"></span>//如果是GET方法<span style="white-space:pre"></span>if(req.getMethod().equalsIgnoreCase("get")){<span style="white-space:pre"></span>//用请求包装类将req对象进行包装<span style="white-space:pre"></span>EncodingRequestWrapper wrapper = new EncodingRequestWrapper(req,encoding);<span style="white-space:pre"></span>//传递包装后的req对象给过滤链<span style="white-space:pre"></span>filterChain.doFilter(wrapper,response);<span style="white-space:pre"></span>} else {<span style="white-space:pre"></span>//设置请求字符编码<span style="white-space:pre"></span>request.setCharacterEncoding(encoding);<span style="white-space:pre"></span>//设置内容类型 + 字符编码<span style="white-space:pre"></span>response.setContentType("text/html;charset="+encoding);<span style="white-space:pre"></span>//传递请求给过滤链<span style="white-space:pre"></span>filterChain.doFilter(request,response);<span style="white-space:pre"></span>}<span style="white-space:pre"></span>} <span style="white-space:pre"></span>@Override<span style="white-space:pre"></span>public void init(FilterConfig filterConfig) throws ServletException {<span style="white-space:pre"></span>//获取过滤器的初始参数<span style="white-space:pre"></span>encoding = filterConfig.getInitParameter("encoding");<span style="white-space:pre"></span>}}/** * 请求包装类 * */class EncodingRequestWrapper extends HttpServletRequestWrapper{<span style="white-space:pre"></span>HttpServletRequest request;<span style="white-space:pre"></span>String encoding = null;<span style="white-space:pre"></span>/**<span style="white-space:pre"></span>* 不进行字符编码设置的包装<span style="white-space:pre"></span>* @param request<span style="white-space:pre"></span>*/<span style="white-space:pre"></span>public EncodingRequestWrapper(HttpServletRequest request){<span style="white-space:pre"></span>super(request);<span style="white-space:pre"></span>this.request = request;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>/**<span style="white-space:pre"></span>* 设置字符编码的包装<span style="white-space:pre"></span>* @param request<span style="white-space:pre"></span>* @param encoding<span style="white-space:pre"></span>*/<span style="white-space:pre"></span>public EncodingRequestWrapper(HttpServletRequest request, String encoding){<span style="white-space:pre"></span>super(request);<span style="white-space:pre"></span>this.request = request;<span style="white-space:pre"></span>this.encoding = encoding;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>/**<span style="white-space:pre"></span>* 重写请求对象的getParameter方法:加上字符编码设置<span style="white-space:pre"></span>* <span style="white-space:pre"></span>*/<span style="white-space:pre"></span>@Override<span style="white-space:pre"></span>public String getParameter(String name){<span style="white-space:pre"></span>//获取传递的参数值<span style="white-space:pre"></span>String value = request.getParameter(name);<span style="white-space:pre"></span>if(value!=null){<span style="white-space:pre"></span>try{<span style="white-space:pre"></span>//参数值获取,以容器默认的字符编码ISO8859-1“读取”,并以配置里设定的字符编码进行编码<span style="white-space:pre"></span>value = new String(value.getBytes("ISO8859-1"),encoding);<span style="white-space:pre"></span>}catch(UnsupportedEncodingException e){<span style="white-space:pre"></span>e.printStackTrace();<span style="white-space:pre"></span>}<span style="white-space:pre"></span>}<span style="white-space:pre"></span>return value;<span style="white-space:pre"></span>}}
值得注意的是这里面使用了请求包装类(ServletWrapper),作为Filter的助手对请求消息进行处理。