??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
JSP
技术构建在
Servlet
技术之上,所?/span>
Servlet
?/span>
JSP
的技术本质是一LQ?/span>
JSP
能做到的Q?/span>
Servlet
都能做到Q但是它们却各有所ѝ?/span>
Servlet
比较适合作ؓ控制cȝӞ比如视图控制器等。另外,
Servlet
q可以作滤器、监听器{?/span>
Servlet
不仅可以动态生?/span>
HTML
内容Q还可以动态生成图形。总而言之,
Servlet
在项目中作ؓ控制cȝlgQƈ且处理一些后C务,
JSP
则作为显C组件?/span>
在本节,我们介l?/span>
Servlet
常用的用方法之一Q作滤器。在
Servlet
作ؓqo器用时Q它可以对客Lhq行qo处理Q当它处理完成后Q它会交l下一个过滤器处理Q就q样Q客Lh在过滤链里一个个处理Q直到请求发送到目标。D个例子,某个|站里有提交
"
修改的注册信?/span>
"
的网,当用户填写完成修改信息ƈ提交后,服务端在q行真正的处理时需要做两个处理Q客L的会话是否有效;Ҏ交的数据q行l一的编码,比如
GB2312
。这两个处理可以在由两个qo器组成的qoNq行处理。当qo器处理成功后Q把提交的数据发送到最l目标;如果qo器处理不成功Q比如客L的会话无效)Q它把视图z֏到指定的错误面。可以看出,qo器就像一扇门Q客L要和服务端的某个目标交互Q必通过q扇门?/span>
下面我们来看一个具体的例子Q这个例子将介绍怎么开发过滤器Qƈ且介l怎么?/span>
web.xml
文g里配|过滤器。这个例子里有两?/span>
JSP
面Q前一个页面用戯入一些信息然后提交,后一个页面显C用h交的信息。在提交信息后,要经q两个过滤器的处理,一个检查用h否登录,一个把用户的提交信息用
GB2312
q行重新~码?/span>
开发一?span lang="EN-US">FilterQ这个Filter需要实现Filter接口QFilter接口定义了以下的ҎQ?
destroy() //由Web容器调用Q销毁此Filter
init(FilterConfig filterConfig) ///由Web容器调用Q初始化此Filter
doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)//具体qo处理代码
下面我们来看Ҏ交信息用GB2312q行重新~码的FilterQ见CZ14-7、示?4-8?
【程序源代码】?
1 // ==================== Program Discription =====================
2 // E序名称Q示?4-7 : EncodingFilter .java
3 // E序目的Q学习用编码过滤器
4 // ==============================================================
5 import javax.servlet.FilterChain;
6 import javax.servlet.ServletRequest;
7 import javax.servlet.ServletResponse;
8 import java.io.IOException;
9 import javax.servlet.Filter;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12 import javax.servlet.ServletException;
13 import javax.servlet.FilterConfig;
14
15 public class EncodingFilter implements Filter
16 {
17
18 private String targetEncoding = "gb2312";
19 protected FilterConfig filterConfig;
20
21 public void init(FilterConfig config) throws ServletException {
22 this.filterConfig = config;
23 this.targetEncoding = config.getInitParameter("encoding");
24 }
25
26
27 public void doFilter(ServletRequest srequest,
ServletResponse sresponse,FilterChain chain)
28 throws IOException, ServletException {
29
30 HttpServletRequest request = (HttpServletRequest)srequest;
31 request.setCharacterEncoding(targetEncoding);//把请求用指定的方式编?
32 // 把处理发送到下一个过滤器
33 chain.doFilter(srequest,sresponse);
34 }
35
36 public void destroy()
37 {
38 this.filterConfig=null;
39 }
40
41 public void setFilterConfig(final FilterConfig filterConfig)
42 {
43 this.filterConfig=filterConfig;
44 }
45 }
【程序源代码】?
1 // ==================== Program Discription =====================
2 // E序名称Q示?4-8 : LoginFilter.java
3 // E序目的Q学习用登录过滤器
4 // ==============================================================
5 import javax.servlet.FilterChain;
6 import javax.servlet.ServletRequest;
7 import javax.servlet.ServletResponse;
8 import java.io.IOException;
9 import javax.servlet.Filter;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12 import javax.servlet.ServletException;
13 import javax.servlet.FilterConfig;
14
15 public class LoginFilter implements Filter
16 {
17 String LOGIN_PAGE="init.jsp";
18 protected FilterConfig filterConfig;
19 public void doFilter(final ServletRequest req,final ServletResponse
res,FilterChain chain)throws IOException,ServletException
20 {
21 HttpServletRequest hreq = (HttpServletRequest)req;
22 HttpServletResponse hres = (HttpServletResponse)res;
23 String isLog=(String)hreq.getSession().getAttribute("isLog");
24 if((isLog!=null)&&((isLog.equals("true"))||(isLog=="true")))//查是否登?
25 {
26 chain.doFilter(req,res);
27 return ;
28 }
29 else
30 hres.sendRedirect(LOGIN_PAGE);//如果没有dQ把视图z֏到登录页?
31 }
32
33 public void destroy()
34 {
35 this.filterConfig=null;
36 }
37 public void init(FilterConfig config)
38 {
39 this.filterConfig=config;
40 }
41 public void setFilterConfig(final FilterConfig filterConfig)
42 {
43 this.filterConfig=filterConfig;
44 }
45 }
【程序注解?
正如前面所_EncodingFilter的目的是把客L的请求用指定的方式编码,具体的处理在request.setCharacterEncoding(targetEncoding)完成了。LoginFilter判断用户在进入目标之前是否登录,if((isLog!=null)&&((isLog.equals("true"))||(isLog=="true")))检查用h否登录,如果已登录,那么把视图让qo铄l处理,如果没有dQ把视图z֏到登录页面,qo铑֤理结束?
下面我们来看怎么在web.xml里配|这两个qo器,代码如下所C:
【程序源代码】?
<web-app>
<filter>
<filter-name>encoding</filter-name>
<filter-class>EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>gb2312</param-value>
</init-param>
</filter>
<filter>
<filter-name>auth</filter-name>
<filter-class>LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>auth</filter-name>
<url-pattern>/target.jsp</url-pattern>
</filter-mapping>
</web-app>
【程序注解?
可以看出Q配|FilterӞ首先指定Filter的名字和Filter的实现类Q如果有必要Q还要配|Filter的初始参敎ͼ然后为Filter做映,q个映射指定了需要过滤的目标QJSP、ServletQ。在上面的例子中Q指定了EncodingFilter 为所有的JSP和Servlet做过滤,LoginFilter为target.jsp做过滤。这P当客戯求target.jspӞ首先要经qEncodingFilter的处理,然后l过LoginFilter的处理,最后才把请求传递给target.jsp。?
【运行程序?
把程序部|到Web服务器里Q比如TomcatQ,然后启动Web服务器,在浏览器里输入以下URLQ根据具体请求改变URLQ:http://127.0.0.1:8080/ch14/target.jsp
那么Filter会把视图派发到Q?a target="_blank">http://127.0.0.1:8080/ch14/init.jsp
在init.jsp里,我们使用Q?
<% session.setAttribute("isLog","true");%>
来设|用户已l登录(q里是简化的Q在实际目中,可能要经q验证处理)。在init.jsp里,可以提交一些中文的信息。由于提交的信息被EncodingFilter使用GB2312l一~码了,故在target.jsp里能够正显CZ文。您可以做一个试验,把?
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
改ؓ
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/nothing</url-pattern>
</filter-mapping>
然后重新启动Web服务器。那么在target.jsp里,中文不能正显C?o:p>
servlet和JSPqo器Filter
或许Qservlet API?.3版本中最重要的一个新功能是能够为servlet和JSP面定义qo器。过滤器提供了某些早期服务器所支持的非标准“servlet链接”的一U功能强大且标准的替代品?br />qo器是一个程序,它先于与之相关的servlet或JSP面q行在服务器上。过滤器可附加到一个或多个servlet或JSP面上,q且可以查进入这些资源的h信息。在q之后,qo器可以作如下的选择Q?br />l 以常规的方式调用资源Q即Q调用servlet或JSP面Q?br />l 利用修改q的h信息调用资源?br />l 调用资源Q但在发送响应到客户机前对其q行修改
l L该资源调用,代之以{到其他的资源Q返回一个特定的状态代码或生成替换输出?br />qo器提供了几个重要好处?br />首先Q它以一U模块化的或可重用的方式装公共的行为。你?0个不同的serlvet或JSP面Q需要压~它们的内容以减下载时间吗Q没问题Q构造一个压~过滤器Q参阅第11节)Q然后将它应用到30个资源上卛_?br />其次Q利用它能够高U访问决{与表现代码相分R这对于JSP特别有h|其中一般希望将几乎整个面集中在表CQ而不是集中在业务逻辑上。例如,希望d来自某些站点的访问而不用修改各面Q这些页面受到访问限Ӟ吗?没问题:建立一个访问限制过滤器Q参阅第8节)q把它应用到惌限制讉K的页面上卛_?br />最后,qo器你能够对许多不同的资源进行批量性的更改。你有许多现存资源,q些资源除了公司名要更改外其他的保持不变Q能办到么?没问题:构造一个串替换qo器(参阅W?0节)Q只要合适就使用它?br />但要注意Q过滤器只在与servlet规范2.3版兼容的服务器上有作用。如果你的Web应用需要支持旧版服务器Q就不能使用qo器?br />1Q 徏立基本过滤器
建立一个过滤器涉及下列五个步骤Q?br />1Q徏立一个实现Filter接口的类。这个类需要三个方法,分别是:doFilter、init和destroy。doFilterҎ包含主要的过滤代码(见第2步)QinitҎ建立讄操作Q而destroyҎq行清楚?br />2Q在doFilterҎ中放入过滤行为。doFilterҎ的第一个参CؓServletRequest对象。此对象l过滤器提供了对q入的信息(包括表单数据、cookie和HTTPh_的完全访问。第二个参数为ServletResponseQ通常在简单的qo器中忽略此参数。最后一个参CؓFilterChainQ如下一步所qͼ此参数用来调用servlet或JSPc?br />3Q调用FilterChain对象的doFilterҎ。Filter接口的doFilterҎ取一个FilterChain对象作ؓ它的一个参数。在调用此对象的doFilterҎӞȀzM一个相关的qo器。如果没有另一个过滤器与servlet或JSP面兌Q则servlet或JSP面被激zR?br />4Q对相应的servlet和JSP面注册qo器。在部v描述W文Ӟweb.xmlQ中使用filter和filter-mapping元素?br />5Q禁用激zdservlet。防止用户利用缺省servlet URLl过qo器设|?br />1.1 建立一个实现Filter接口的类
所有过滤器都必d现javax.servlet.Filter。这个接口包含三个方法,分别为doFilter、init和destroy?br />l public void doFilter(ServletRequset request,
ServletResponse response,
FilterChain chain)
thows ServletException, IOException
每当调用一个过滤器Q即Q每ơ请求与此过滤器相关的servlet或JSP面Q时Q就执行其doFilterҎ。正是这个方法包含了大部分过滤逻辑?br />W一个参Cؓ与传入请求有关的ServletRequest。对于简单的qo器,大多数过滤逻辑是基于这个对象的。如果处理HTTPhQƈ且需要访问诸如getHeader或getCookies{在ServletRequest中无法得到的ҎQ就要把此对象构造成HttpServletRequest?br />W二个参CؓServletResponse。除了在两个情Ş下要使用它以外,通常忽略q个参数。首先,如果希望完全d对相关servlet或JSP面的访问。可调用response.getWriterq直接发送一个响应到客户机。第7节给l内容,W?节给Z个例子。其ơ,如果希望修改相关的servlet或JSP面的输出,可把响应包含在一个收集所有发送到它的输出的对象中。然后,在调用serlvet或JSP面后,qo器可查输出,如果合适就修改它,之后发送到客户机。详情请参阅W?节?br />DoFilter的最后一个参CؓFilterChain对象。对此对象调用doFilter以激zMservlet或JSP面相关的下一个过滤器。如果没有另一个相关的qo器,则对doFilter的调用激zservlet或JSP本n?br />l public void init(FilterConfig config)
thows ServletException
initҎ只在此过滤器W一ơ初始化时执行,不是每次调用qo器都执行它。对于简单的qo器,可提供此Ҏ的一个空体,但有两个原因需要用init。首先,FilterConfig对象提供对servlet环境及web.xml文g中指zqo器名的访问。因此,普遍的办法是利用initFilterConfig对象存放在一个字D中Q以便doFilterҎ能够讉Kservlet环境或过滤器名。这U处理在W?节描q。其ơ,FilterConfig对象h一个getInitParameterҎQ它能够讉K部v描述W文Ӟweb.xmlQ中分配的过滤器初始化参数。初始化参数的用在W?节中描述?br />l public void destroy( )
此方法在利用一个给定的qo器对象永久地l止服务器(如关闭服务器Q时调用。大多数qo器简单地为此Ҏ提供一个空体,不过Q可利用它来完成诸如关闭qo器用的文g或数据库q接池等清除d?br />1.2 过滤行为放入doFilterҎ
doFilterҎ为大多数qo器地关键部分。每当调用一个过滤器Ӟ都要执行doFilter。对于大多数qo器来_doFilter执行的步骤是Z传入的信息的。因此,可能要利用作为doFilter的第一个参数提供的ServletRequest。这个对象常常构造ؓHttpServletRequestcdQ以提供对该cȝ更特D方法的讉K?br />1.3 调用FilterChain对象的doFilterҎ
Filter接口的doFilterҎ以一个FilterChain对象作ؓ它的W三个参数。在调用该对象的doFilterҎӞȀzM一个相关的qo器。这个过E一般持l到链中最后一个过滤器为止。在最后一个过滤器调用其FilterChain对象的doFilterҎӞȀzservlet或页面自w?br />但是Q链中的Lqo器都可以通过不调用其FilterChain的doFilterҎ中断q个q程。在q样的情况下Q不再调用JSP面的serlvetQƈ且中断此调用q程的过滤器负责输出提供给客户机。详情请参阅W?节?br />1.4 寚w当的servlet和JSP面注册qo?br />部v描述W文件的2.3版本引入了两个用于过滤器的元素,分别是:filter和filter-mapping。filter元素向系l注册一个过滤对象,filter-mapping元素指定该过滤对象所应用的URL?br />1.filter元素
filter元素位于部v描述W文Ӟweb.xmlQ的前部Q所有filter-mapping、servlet或servlet-mapping元素之前。filter元素h如下六个可能的子元素Q?br />l icon q是一个可选的元素Q它声明IDE能够使用的一个图象文件?br />l filter-name q是一个必需的元素,它给qo器分配一个选定的名字?br />l display-name q是一个可选的元素Q它l出IDE使用的短名称?br />l description q也是一个可选的元素Q它l出IDE的信息,提供文本文?br />l filter-class q是一个必需的元素,它指定过滤器实现cȝ完全限定名?br />l init-param q是一个可选的元素Q它定义可利用FilterConfig的getInitParameterҎd的初始化参数。单个过滤器元素可包含多个init-param元素?br />h意,qo是在serlvet规范2.3版中初次引入的。因此,web.xml文g必须使用DTD?.3版本。下面介l一个简单的例子Q?br /> < xml version="1.0" encoding="ISO-8859-1" >
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
" <web-app>
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>myPackage.FilterClass</filter-class>
</filter>
<!-- ... -->
<filter-mapping>...</filter-mapping>
</web-app>
2.filter-mapping元素
filter-mapping元素位于web.xml文g中filter元素之后serlvet元素之前。它包含如下三个可能的子元素Q:
l filter-name q个必需的元素必M用filter元素声明时给予过滤器的名U相匚w?br />l url-pattern 此元素声明一个以斜杠Q?Q开始的模式Q它指定qo器应用的URL。所有filter-mapping元素中必L供url-pattern或servlet-name。但不能对单个filter-mapping元素提供多个url-pattern元素V如果希望过滤器适用于多个模式,可重复整个filter-mapping元素?br />l servlet-name 此元素给Z个名Uͼ此名U必M利用servlet元素l予servlet或JSP面的名U相匚w。不能给单个filter-mapping元素提供多个servlet-name元素V如果希望过滤器适合于多个servlet名,可重复这个filter-mapping元素?br />下面举一个例子:
< xml version="1.0" encoding="ISO-8859-1" >
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
" <web-app>
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>myPackage.FilterClass</filter-class>
</filter>
<!-- ... -->
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/someDirectory/SomePage.jsp</url-pattern>
</filter-mapping>
</web-app>
1.5 用Ȁzdservlet
在对资源应用qo器时Q可通过指定要应用过滤器的URL模式或servlet名来完成。如果提供servlet名,则此名称必须与web.xml的servlet元素中给出的名称相匹配。如果用应用到一个serlvet的URL模式Q则此模式必M利用web.xml的元素servlet-mapping指定的模式相匚w。但是,多数服务器用“激zdservlet”ؓservlet体统一个缺省的URLQ?a href="http://host/WebAppPrefix/servlet/ServletName">http://host/WebAppPrefix/servlet/ServletName。需要保证用户不利用q个URL讉KservletQ这样会l过qo器设|)?br />例如Q假如利用filter和filter-mapping指示名ؓSomeFilter的过滤器应用到名为SomeServlet的servletQ则如下Q?br /> <filter>
<filter-name>SomeFilter</filter-name>
<filter-class>somePackage.SomeFilterClass</filter-class>
</filter>
<!-- ... -->
<filter-mapping>
<filter-name>SomeFilter</filter-name>
<servlet-name>SomeServlet</servlet-name>
</filter-mapping>
接着Q用servlet和servlet-mapping规定URL http://host/webAppPrefix/Blah 应该调用SomeSerlvetQ如下所C:
<filter>
<filter-name>SomeFilter</filter-name>
<filter-class>somePackage.SomeFilterClass</filter-class>
</filter>
<!-- ... -->
<filter-mapping>
<filter-name>SomeFilter</filter-name>
<servlet-name>/Blah</servlet-name>
</filter-mapping>
现在Q在客户Z用URL http://host/webAppPrefix/Blah 时就会调用过滤器。过滤器不应用到
http://host/webAppPrefix/servlet/SomePackage.SomeServletClass?br />管有关闭激zd的服务器专用Ҏ。但是,可移植最强的Ҏ旉新映Web应用钟的/servlet模式Q这样所有包含此模式的请求被送到相同的servlet中。ؓ了重新映此模式Q首先应该徏立一个简单的servletQ它打印一条错误消息,或重定向用户到顶层页。然后,使用servlet和servlet-mapping元素发送包?servlet模式的请求到该servlet。程序清?-1l出了一个简短的例子?/p>
E序清单9-1 web.xmlQ重定向~省servlet URL的摘录)
< xml version="1.0" encoding="ISO-8859-1" >
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
" <web-app>
<!-- ... -->
<servlet>
<servlet-name>Error</servlet-name>
<servlet-class>somePackage.ErrorServlet</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name>Error</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>
2Q 样例:报告qo?br />热打铁Q我们来试验一个简单的qo器,只要调用相关的servlet或JSP面Q它打C条消息到标准输出。ؓ了完成此dQ相应的qo器必d有下面的内容Q?br />1Q实现Filter接口的一个类。这个类名ؓReportFilterQ如E序清单9-2所C。这个类对init和destroyҎ提供IZ?br />2Q在doFilterҎ中过滤行为。每当调用与q个qo器相关的servlet或JSP面ӞdoFilterҎq成一个打印输出,此输出列求主机和调用的URL。因为getRequestURLҎ位于HttpServletRequest而不是ServletRequest中,所以把ServletRequest对象构造ؓHttpServletRequestcd?br />3Q调用FilterChain的doFilterҎ。在打印输出报告后,qo器调用FilterChain的doFilterҎȀzservlet或JSP面Q如果有的话Q调用下一个过滤器Q?br />4Q对Web应用主页和显CTodaysSpecialservletq行注册。首先,filter元素名UReporter与类moreservlets.filters.ReportFilter相关联。然后,filter-mapping元素使用/index.jsp的url-pattern过滤器与主늛兌。最后,filter-mapping元素使用TodaysSpecial的servlet-name过滤器与TodaysSpecialservletQ名UTodaysSpecial是在servlet元素中声明的Q相兌。参见程序清?-3?br />5Q禁用激zdservlet。首先,建立一个RedirectorServletQ见E序清单9-6Q,它把接收到的所有请求重定向到此Web应用的主c接着Q利用servlet和servlet-mapping元素Q参见程序清?-3Q指定所有以http://host/webAppPrefix/servlet/ 开始的URL都应该激zRedirectorServlet?br />l出q些讄后,每当客户求此Web应用主页Q程序清?-4Q或TodaysSpecialservletQ程序清?-5Q时Q都调用此过滤器?/p>
E序清单9-2 ReportFilter.java
package moreservlets.filters;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*; // For Date class
/** Simple filter that prints a report on the standard output
* each time an associated servlet or JSP page is accessed.
*/
public class ReportFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest)request;
System.out.println(req.getRemoteHost() +
" tried to access " +
req.getRequestURL() +
" on " + new Date() + ".");
chain.doFilter(request,response);
}
public void init(FilterConfig config)
throws ServletException {
}
public void destroy() {}
}
E序清单9-3 web.xmlQ针Ҏ告过滤器的摘录)
< xml version="1.0" encoding="ISO-8859-1" >
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
" <web-app>
<!-- Register the name "Reporter" for ReportFilter. -->
<filter>
<filter-name>Reporter</filter-name>
<filter-class>
moreservlets.filters.ReportFilter
</filter-class>
</filter>
<!-- ... -->
<!-- Apply the Reporter filter to home page. -->
<filter-mapping>
<filter-name>Reporter</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
<!-- Also apply the Reporter filter to the servlet named
"TodaysSpecial".
-->
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>TodaysSpecial</servlet-name>
</filter-mapping>
<!-- ... -->
<!-- Give a name to the Today's Special servlet so that filters
can be applied to it.
-->
<servlet>
<servlet-name>TodaysSpecial</servlet-name>
<servlet-class>
moreservlets.TodaysSpecialServlet
</servlet-class>
</servlet>
<!-- ... -->
<!-- Make /TodaysSpecial invoke the servlet
named TodaysSpecial (i.e., moreservlets.TodaysSpecial).
-->
<servlet-mapping>
<servlet-name>TodaysSpecial</servlet-name>
<url-pattern>/TodaysSpecial</url-pattern>
</servlet-mapping>
<!-- Turn off invoker. Send requests to index.jsp. -->
<servlet-mapping>
<servlet-name>Redirector</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>