过滤器

一、过滤器简介
a)Filter是SUN公司提供的一个资源过滤器接口,不同的Web容器有着不同的实现
b)Filter位于Web服务器和Web资源(Servlet/Jsp/Html)之间
c)过滤器过滤请求和响应二者
d)Filter可以进行简单判段,是否将请求放行给Web资源
e)Filter的开发过程:
1>>类 implements javax.servlet.Filter接口
2>>在web.xml文件配置Filter过滤器,告之Web服务器有过滤器的存在
web.xml中的配置信息如下:

<filter>
<filter-name>FilterDemo1</filter-name>(过滤器,可以随意,但要和filter-mapping中的name一致)
<filter-class>cn.itcast.web.filter.FilterDemo1</filter-class>(过滤器全路径)
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>(过滤器名,同上)
<url-pattern>/*</url-pattern>(过滤器能够过滤的资源路径,不是用户在URL中访问的路径) \\\*/
</filter-mapping>

  



注意:
1)当访问一个web资源时,没有得到对应的结果,有可能是Filter没有放行资源
2)总结:写Filter一定要知道该Filter过滤哪个或哪些资源,不是所有的Filter都过滤/*的资源。 \\\*/

3 过滤器链
a)一个Web应用可以有0个或多个Filter,多个Filter的组合就是过滤器链
b)**多个Filter的执行先后顺序,与web.xml文件中配置的顺序有关
c)chain.doFilter(request,response)具有二义性:
>>如果有下一个Filter时,将请求转发给下一个Filter
>>如果无下一个Filter时,将请求转发给Web资源(serlvet/jsp/html)
d)可以将web资源中的一些公共代码,提取出来,放入Filter中

4 过滤器生命周期(Filter是一个单例)
空参构造() 1次
||
init() 1次
||
doFilter(请求,响应,过滤器链) N次,与请求次数有关
||
destory() 1次

*5 Filter的案例
a)简单的登录页面:设置请求和响应的编码方式
init()可以获取到初始化的配置信息,因此将POST请求方式的中文的编码进行设置,
防止提交中文和响应中文时的乱码问题。

 1 web.xml:
 2 <filter>
 3 <filter-name>FilterDemo1</filter-name>
 4 <filter-class>com.suse.servlet.FilterDemo1</filter-class>
 5 <init-param>
 6 <param-name>charset</param-name>
 7 <param-value>utf-8</param-value>
 8 </init-param>
 9 </filter>
10 <filter-mapping>
11 <filter-name>FilterDemo1</filter-name>
12 <url-pattern>/*</url-pattern> //////*/
13 </filter-mapping>
14 
15 fileterCode:
16 private FilterConfig filterConfig;
17 @Override
18 public void init(FilterConfig filterConfig) throws ServletException {
19 this.filterConfig = filterConfig;//设置filterConfig值
20 }
21 
22 @Override
23 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
24 String charSet = this.filterConfig.getInitParameter("charset");//得到配置文件的编码方式
25 request.setCharacterEncoding(charSet);//设置请求时编码
26 response.setContentType("text/html;charset="+charSet);//设置响应时编码
27 filterChain.doFilter(request, response);//放行
28 }

 

b)静态资源和动态资源进行不同的缓存处理,代码如下:

 1 if(uri!=null && uri.endsWith("jsp")){
 2 //NO3如果是动态资源,设置三个响应头通知浏览器不缓存
 3 response.setDateHeader("Expires", -1);//IE
 4 response.setHeader("Cache-Control", "no-cache");
 5 response.setHeader("Pragma", "no-cache");
 6 }else if(uri!=null && uri.endsWith("html")){
 7 //NO4如果是静态资源,缓存一定的时间
 8 String strHtml = filterConfig.getInitParameter("html");
 9 long time = System.currentTimeMillis()+Integer.parseInt(strHtml)*1000;
10 //time为毫秒值
11 response.setDateHeader("expires",time); //第二个参数为保存到的时间,单位为毫秒
12 response.setHeader("cache-control",time/1000+"");//第二个参数为保存到的时间,单位为秒
13 response.setHeader("pragma",time/1000+"");//第二个参数为保存到的时间,单位为秒
14 }

 

c)通过Filter实现URL级别的权限认证 ———— 对敏感目录进行认证
//访问admin目录下的admin.html文件需要带上用户名和密码进行验证

d)通过Filter和cookie实现自动登录功能

 1 xml配置:
 2 <filter>
 3 <filter-name>AutoLoginFilter</filter-name>
 4 <filter-class>com.suse.filter.AutoLoginFilter</filter-class>
 5 </filter>
 6 <filter-mapping>
 7 <filter-name>AutoLoginFilter</filter-name>
 8 <url-pattern>/welcome.jsp</url-pattern>
 9 </filter-mapping>
10 第一次登录,设置cookie:
11 String username = request.getParameter("username");
12 String password = request.getParameter("password");
13 if (username != null && password != null && username.equals("jack") && password.equals("123")) {
14 Cookie cookie = new Cookie("usernameApassword", username + "_" + password);
15 cookie.setMaxAge(10 * 60);
16 response.addCookie(cookie);
17 request.setAttribute("username", username);
18 request.getRequestDispatcher("/welcome.jsp").forward(request, response);
19 } else {
20 response.getWriter().write("登录失败!");
21 }
22 第二次登录,解析cookie:
23 @Override
24 public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
25 HttpServletRequest request = (HttpServletRequest) req;
26 HttpServletResponse response = (HttpServletResponse) res;
27 String username = null;
28 String password = null;
29 Cookie[] cookies = request.getCookies();
30 for (Cookie cookie : cookies) {
31 if (cookie.getName().equalsIgnoreCase("usernameApassword")) {
32 String str = cookie.getValue();
33 String[] usernameApassword = str.split("_");
34 username = usernameApassword[0];
35 password = usernameApassword[1];
36 }
37 }
38 if (username != null && password != null && username.equals("jack") && password.equals("123")) {
39 request.setAttribute("username", username);
40 filterChain.doFilter(request, response);
41 } else {
42 request.getRequestDispatcher("/login.jsp").forward(request, response);
43 }
44 }

 

 

二、映射Filter的细节

a)在默认情况下,Filter只过滤Request的请求,即:web资源之间的转包和包含不过滤.
  MappingFilter::doFilter():A
  FromServlet::doGet()
  ToServlet::doGet()
  MappingFilter::doFilter():B

b)当需要过滤forward请求的资源时,可以设置dispatcher为FORWARD过滤方式
(注意:默认的REQUEST方式便不存在了)

  FromServlet::doGet()
  MappingFilter::doFilter():A
  ToServlet::doGet()
  MappingFilter::doFilter():B

在web.xml文件中配置代码如下:

<filter-nameapping>
<filter-name>MappingFilter</filter-name>
<url-pattern>/ToServlet</url-pattern> 
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

 

c)即过滤REQUEST又过滤FORWARD时:

<filter-nameapping>
<filter-name>MappingFilter</filter-name>
<url-pattern>/ToServlet</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

 

d)当需要过滤include请求的资源时,可以设置dispatcher为INCLUDE过滤方式
e)当需要过滤error请求的资源时,可以设置dispatcher为ERROR过滤方式
注意:此时,一定在要web.xml文件声明错误代码或类型

<error-page>
<error-code>500</error-code>
<location>/sys_500.jsp</location>
</error-page>    

<filter-mapping>
<filter-name>MappingFilter</filter-name>
<url-pattern>/*</url-pattern> */
<dispatcher>ERROR</dispatcher>
</filter-mapping>

 


f)对于过滤Servlet资源时,即可使用url-pattern,又可以使用servlet-name
g)一个Filter可以过滤1个或N个资源,
即:一个<filter>,多个<filter-mapping>


*2 装饰设计模式
a)当某个类的某个方法不适应当前业务的需要
思路:
1》扩展父类的可供扩展的方法,可以使有,但不优
2》装饰设计模式(推荐)

开发步骤:
  1)写一个普通类(非web应用)或写一个普通类扩展[extends]一个父类(web应用)
  2)写一个需要被包装的实例变量
  3)通过构造方式为被包装的实例变量赋值
  4)对于不满足需求的方法,重写父类的相关方法
[可选]5)对于满足需求的方法,直接调用被包装的对象

code:

1,带有行号的BufferedReader的ReadLine方法的类:MyBufferedReader

 1 public class MyBufferedReader {
 2 
 3 private BufferedReader bufferedReader;
 4 
 5 private Integer lineNum;
 6 
 7 public MyBufferedReader(BufferedReader bufferedReader) {
 8 this.lineNum = 1;
 9 this.bufferedReader = bufferedReader;
10 }
11 
12 
13 //包装readLine()方法
14 public String readLine() throws IOException {
15 String read = null;
16 String line = null;
17 if ((line = bufferedReader.readLine()) != null) {
18 read = this.lineNum + ":" + line;
19 lineNum++;
20 }
21 return read;
22 }
23 
24 //关闭文件流,使用父类的方法来关闭流
25 public void close() throws IOException {
26 bufferedReader.close();
27 } 
28 }

 

2,增强Date类的toLocalString()方法,显示类似信息 "XXXX年XX月XX日 星期X XX:XX:XX"

 1 public class MyDate {
 2 
 3 private Date date;
 4 
 5 public MyDate(Date date) {
 6 this.date = date;
 7 }
 8 
 9 public String toLocalString() {
10 /*SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 EEEE HH:mm:ss ");
11 return dateFormat.format(date);*/
12 DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM, Locale.CHINA);
13 return dateFormat.format(date); 
14 }
15 }

 

 


*3 Filter案例

a)对request进行装饰,完全解决get、post请求方式下的乱码问题

 1 //MyRequest.java
 2 public class MyRequest extends HttpServletRequestWrapper {
 3 
 4 private HttpServletRequest request;
 5 
 6 public MyRequest(HttpServletRequest request) {
 7 super(request);
 8 this.request = request;
 9 }
10 
11 @Override
12 public String getParameter(String name) {
13 String value = null;
14 //获取请求方式[GET/POST]
15 String method = request.getMethod();
16 if("GET".equals(method)) {
17 try {
18 value = request.getParameter(name);
19 byte[] buf = value.getBytes("ISO8859-1");
20 value = new String(buf, "utf-8");
21 } catch (UnsupportedEncodingException e) {
22 e.printStackTrace();
23 }
24 } else {
25 try {
26 request.setCharacterEncoding("utf-8");
27 value = request.getParameter(name);
28 } catch (UnsupportedEncodingException e) {
29 e.printStackTrace();
30 }
31 }
32 return value;
33 }
34 
35 //EncodingFilter
36 public class EncodingFilter implements Filter {
37 @Override
38 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
39 HttpServletRequest request = (HttpServletRequest) req;
40 HttpServletResponse response = (HttpServletResponse) res;
41 chain.doFilter(new MyRequest(request), response);
42 }
43 }

 

b)对request进行装饰,实现html标签的转义功能
//转义html符号

 1 private String filter(String message) {
 2 if (message == null)
 3 return (null);
 4 char content[] = new char[message.length()];
 5 message.getChars(0, message.length(), content, 0);
 6 StringBuffer result = new StringBuffer(content.length + 50);
 7 for (int i = 0; i < content.length; i++) {
 8 switch (content[i]) {
 9 case '<':
10 result.append("&lt;");
11 break;
12 case '>':
13 result.append("&gt;");
14 break;
15 case '&':
16 result.append("&amp;");
17 break;
18 case '"':
19 result.append("&quot;");
20 break;
21 default:
22 result.append(content[i]);
23 }
24 }
25 return (result.toString());
26 }

 


c)对response进行装饰[有一定难度]————压缩响应
思路: 缓存 --> 压缩 --> 输出

 1 //MyResponse
 2 public class MyResponse extends HttpServletResponseWrapper {
 3 
 4 private HttpServletResponse response;
 5 private ByteArrayOutputStream bout = new ByteArrayOutputStream();
 6 
 7 public MyResponse(HttpServletResponse response) {
 8 super(response);
 9 this.response = response;
10 }
11 
12 public ServletOutputStream getOutputStream() throws IOException {
13 return new MyServletOutputStream(bout);
14 }
15 
16 public byte[] getBuffer() {
17 return bout.toByteArray();
18 }
19 
20 }
21 
22 class MyServletOutputStream extends ServletOutputStream {
23 
24 private ByteArrayOutputStream bout;
25 
26 public MyServletOutputStream(ByteArrayOutputStream bout) {
27 this.bout = bout;
28 }
29 
30 @Override
31 public void write(int arg0) throws IOException {
32 }
33 
34 public void write(byte[] bytes) throws IOException {
35 bout.write(bytes);
36 bout.flush();
37 }
38 
39 }
40 
41 
42 //Filter
43 public class GzipFilter implements Filter {
44 @Override
45 public void destroy() {
46 
47 }
48 @Override
49 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
50 HttpServletRequest request = (HttpServletRequest) req;
51 HttpServletResponse response = (HttpServletResponse) res;
52 MyResponse myResponse = new MyResponse(response);
53 
54 chain.doFilter(request, myResponse);
55 
56 byte[] data = myResponse.getBuffer();
57 System.out.println("压缩前:" + data.length);
58 
59 ByteArrayOutputStream bout = new ByteArrayOutputStream();
60 GZIPOutputStream gout = new GZIPOutputStream(bout);
61 gout.write(data);
62 gout.flush();
63 gout.close();
64 
65 data = bout.toByteArray();
66 System.out.println("压缩后" + data.length);
67 
68 response.setHeader("content-encoding", "gzip");
69 response.setHeader("content-length", data.length + "");
70 
71 response.getOutputStream().write(data);
72 
73 }
74 
75 @Override
76 public void init(FilterConfig arg0) throws ServletException {
77 // TODO Auto-generated method stub
78 }
79 }

 

c)简单Filter缓存

//单例,一个服务器只有一个ChcheFilter
public class CacheFilter implements Filter {

//示例变量【每个线程都共享】
private Map<String, byte[]> cache = new HashMap<String, byte[]>();

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;

//获取到访问路径,判断是否已经缓存
String uri = request.getRequestURI();
byte[] data = cache.get(uri);
//若没有缓存,从数据库拉取数据
if (null == data) {
System.out.println("没有缓存,从数据库拉取数据!");
MyResponse myResponse = new MyResponse(response);
//放行到servlet,从数据库拉取数据
chain.doFilter(request, myResponse);
//获取数据,并缓存到Map中
data = myResponse.getBuffer();
cache.put(uri, data);
}

//将数据回写给浏览器
response.getOutputStream().write(data);
}

 

 


4 总结Filter和Servlet

a)Filter通常完成一些非核心的业务流程控制
Servlet通常完成一些核心的业务流程控制

b)Filter通常完成一些对Servlet的请求和响应的预先处理控制。
Servlet却不行

c)Filter和Servlet是一个互补的技术,而不是替代技术

 


posted on 2015-01-10 22:58  SkyGood  阅读(216)  评论(0编辑  收藏  举报