定义
一种用于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()。
项目目录结构
每个项目根目录下包含:index.html / index.htm / index.jsphtml,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. 精确匹配访问:http://IP地址或域名地址/项目名/servlet/myServlet<servlet-mapping> <url-pattern>/servlet/myServlet</url-pattern> <servlet-name>MyServlet</servlet-name> </servlet-mapping>
2. 扩展名匹配访问:http://IP地址或域名地址/项目名/任意字符或空.html<servlet-mapping> <url-pattern>*.html</url-pattern> <servlet-name>MyServlet</servlet-name> </servlet-mapping>
3. 路径映射访问:http://IP地址或域名地址/项目名/任意字符或没有<servlet-mapping> <url-pattern>/servlet/*</url-pattern> <servlet-name>MyServlet</servlet-name> </servlet-mapping>
4.默认匹配访问:http://IP地址或域名地址/项目名/<servlet-mapping> <url-pattern>/*</url-pattern> <servlet-name>MyServlet</servlet-name> </servlet-mapping>
<welcome-file-list>标签
用于配置欢迎页面,包含至少一个<welcome-file>子标签。当访问“http://IP地址或域名地址/项目名/”时显示。注:在WebContent文件夹中至少需要有一个欢迎页面,否则访问“http://IP地址或域名地址/项目名/”时将返回404错误。<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>
<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();
获取本机IP地址、域名及web服务的端口。后两个API效果似乎跟第二三个效果一样。public int getServerPort();
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。
请求体
利用返回的BufferedReader对象可以对消息体进行输入和解析。public BufferedReader getReader();
远端信息
public String getRemoteAddr();
public String getRemoteHost();
public int getRemotePort();
获取对端(客户端)的IP地址、域名地址以及端口。
请求派发
public RequestDispatcher getRequestDispatcher(String arg0);
获取请求派发器,下文将说明。
Session
public HttpSession getSession();
都是获取客户端的Session信息,区别在于第二个方法的参数为true时,与第一个方法相同,若已经存在session则返回该session,若没有session则立即创建一个新的session;为false时,若已经存在session则返回session,若没有session则返回null。public HttpSession getSession(boolean createNew);
响应对象
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();
对Locale信息的处理,用于国际化开发。public void setLocale(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();
Attributepublic 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.20getServletContextName返回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);
这些参数配置在<servlet>标签里。public Enumeration getInitParameterNames();
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 A This 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-pattern protected 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方法在原Servlet将请求派发出去之后移交了控制权,响应交由被派发的Servlet进行处理,等被派发的Servlet处理完后直接响应,可能覆盖原Servlet对响应的处理;include方法则在派发的Servlet处理完之后继续进行处理,与之前的处理进行合并等等,然后再进行响应。
forward与include//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); }
include派发:<html><body>This is ServletB</body></html>
输出页面源码://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将被添加到每一个响应头部,告知浏览器(客户端)进行存储。
生成CookieCookie 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包含的唯一一对键值对进行操作。
发送Cookieresponse.addCookie(cookie);
在响应对象中添加Cookie。
获取CookieCookie[] 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应用程序崩溃。
获取上文中request的API中已说明,不再赘述。HttpSession session = request.getSession(); //如有取得,如无则生成并返回 HttpSession session = request.getSession(false); //如有取得,如无返回null
AttributeSession同样有Attribute,作用范围显然就在当前Session的生命周期内。public Object getAttribute(String attrName); public Enumeration getAttributeNames(); public void removeAttribute(String attrName); public void setAttribute(String attrName, Object attrValue);
Session信息public long getCreationTime();
public long getLastAccessedTime();
public String getId();
public boolean isNew();
获取session的创建时间,最近一次使用时间,ID,以及是否为新创建的session。
有效期
获取最大有效期、手动让session失效、设置最大有效期,默认的MaxInactiveInterval是1800s。设置为-1时为永不会超时过期。public int getMaxInactiveInterval(); public void invalidate(); public void setMaxInactiveInterval(int maxInt);
其他
主要包括URL重写以及隐藏字段(<input type='hidden'/>)传递给request等,都是当Cookie被禁用时的迂回手段,就不多说了。
监听器
监听器(Listener)能够捕捉Servlet运行过程中的各种事件(Event),在事件的前后可以进行各种操作以满足需求。事件在诸多接口中进行处理:Servlet Context的事件1. 监听生命周期,ServletContextListener2. 监听Attributes改变,ServletContextAttributeListener
HTTP Session的事件1. 监听生命周期,HttpSessionListener2. 监听Attributes改变,HttpSessionAttributeListener3. 监听Session变动,HttpSessionActivationListener4. 监听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);
两个方法中的事件对象arg0用法一样,即都能获取Context对象。public void contextDestroyed(ServletContextEvent arg0);
ServletContext ctx = arg0.getServletContext();
Attribute改变public void attributeAdded(ServletContextAttributeEvent arg0);
public void attributeReplaced(ServletContextAttributeEvent arg0);
这个事件对象可以获取到被添加、替换、删除的Attribute的名字和值。public void attributeRemoved(ServletContextAttributeEvent arg0);
注:在attributeReplaced方法中,arg0.getValue()获取的是被替换的值,用于替换的值需要:String name = arg0.getName(); Object value = 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);
当预定义对象绑定到Session及解除与Session的绑定时触发,这里的绑定与解绑同Attribute改变有微妙的联系。valueUnbound触发时一定会触发attributeRemoved,反之不一定;valueBound触发时一定会触发attributeAdded,反之也不一定。更进一步说明,只有当实现了HttpSessionBindingListener接口的Attribute对象绑定及解绑时才会触发valueBound和valueUnbound,例:public void valueBound(HttpSessionBindingEvent arg0);
首先创建一个SpecialAttribute实例:public class SpecialAttribute implements HttpSessionBindingListener{ ... }
接着将其绑定至Session对象,与通常的Attribute无异:SpecialAttribute sAttr = new SpecialAttribute();
只有当以上两句执行时,才会触发valueBound和valueUnbound,普通的Attribute改变只会触发Attribute改变事件。session.setAttribute("sAttr", sAttr); session.removeAttribute("sAttr");
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);
与Context和Session的Attribute改变事件类似,不过此处监听的是request的Attribute。public void attributeReplaced(ServletRequestAttributeEvent arg0);
过滤器
顾名思义,Servlet的过滤器就是在Servlet执行某些操作的前后对请求进行预处理,其生命周期与Servlet的生命周期相关。具体来说,过滤器处理的request对象,对请求消息的请求头或请求体进行处理。同一个Servlet可以应用多个过滤器,这些过滤器依照顺序形成一条过滤器链,每个请求需要经过这些链,层层关卡之后才到达Servlet手中。web.xml
单个的Filter配置和Servlet配置类似,与<servlet>标签同级,首先将Filter的名字和类对应起来,接着配置哪些url-pattern或哪些Servlet要应用该Filter,如:针对某个URL规则:针对某个Servlet:<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>
还有其他规则,这套URL匹配的规则跟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>
匹配所有URI:匹配所有jsp:<filter-mapping> <filter-name>MyFilter</filter-name> <!-- 过滤匹配选项 --> <!-- 匹配所有 --> <url-pattern>/*</url-pattern> </filter-mapping>
匹配所有html:<filter-mapping> <filter-name>MyFilter</filter-name> <!-- 匹配所有jsp --> <url-pattern>*.jsp</url-pattern> </filter-mapping>
注:如用一个Filter匹配多个规则,则需要多个<filter-mapping>定义不同规则映射到同一个<filter-name>上,一个<filter-mapping>对应一个过滤规则。<filter-mapping> <filter-name>MyFilter</filter-name> <!-- 匹配所有html --> <url-pattern>*.html</url-pattern> </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();
init主要利用FilterConfig对象读取Filter初始化的参数,包括:public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain);
public String getFilterName();
public String getInitParameter(String paramName);
public Enumeration getInitParameterNames();
destroy没什么说的,Filter生命周期结束时调用。public ServletContext getServletContext();
起过滤作用的是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*Servlet和filter-mapping略。<filter> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter>
EncodingFilter.java值得注意的是这里面使用了请求包装类(ServletWrapper),作为Filter的助手对请求消息进行处理。/** * 监听器类 * */ 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>} }