首页 > 代码库 > 过滤器 & 装饰者模式
过滤器 & 装饰者模式
一.过滤器概述
------------------------------------------------
1.1.什么是过滤器?
Servlet技术规范中, 定义了Servlet、Filter、Listener三门技术, 其中Filter也叫做过滤器,通过过滤器技术,开发人员可以实现用户在访问某个资源之前或之后,对访问的请求和响应进行拦截,从而做一些相关的处理。
过滤器:
◇ 所谓的过滤器, 就是拦截用户对资源的访问
◇ 一个过滤器可以拦截多个资源, 一个资源也可以配置多个过滤器进行拦截
◇ 其实所谓的拦截, 就是将代表请求的request对象和代表响应的response对象拦截下来, 拦截下来后:
◇ 控制是否允许访问 -- 用户登陆之后才能查看自己的订单页面
◇ 在访问资源之前或之后做一些处理 比如: 全站乱码解决
...
===================================================
二.开发过滤器
------------------------------------------------
2.1.开发过滤器的步骤
Servlet API中提供了一个Filter接口, 开发web应用时, 如果编写一个类实现了这个接口, 则这个类就是一个过滤器
(1) 写一个类实现Filter接口, 并实现其中的方法
(2) 在web应用的web.xml中配置过滤器
------------------------------------------------
~~2.2.Filter生命周期:
当服务器启动时, web应用加载后会立即创建出当前web应用中的所有的Filter对象, 创建出来后, 立即调用init方法进行初始化出操作. 从此以后这个Filter对象一直驻留在内存中为后续所拦截的请求服务, 每次过滤到对资源的访问时, 都会执行doFilter这个方法进行拦截处理, 直到服务器关闭或者web应用移出容器为止, 随着web应用的销毁, 过滤器也跟着销毁, 在销毁之前会调用destroy方法执行善后的处理.
------------------------------------------------
2.3.配置过滤器
<filter> -- 配置一个过滤器
<filter-name>FilterDemo1</filter-name>
-- 过滤器的名字
<filter-class>cn.tedu.FilterDemo1</filter-class> -- 过滤器处理类的全路径名
</filter>
<filter-mapping> -- 为指定的过滤器配置要拦截的路径, 一个过滤器可以配置多个<filter-mapping>
<filter-name>FilterDemo1</filter-name> -- 过滤器的名字
<servlet-name>ServletDemo1</servlet-name> -- 拦截哪个名字的Servlet, 可以配置多个
<url-pattern>/servlet/*</url-pattern> -- 要拦截的路径, 路径的写法和Servlet的<url-pattern>写法一致, 可以配置多个
<dispatcher>REQUEST</dispatcher> -- 配置拦截哪种方式的对资源的访问, 可以取值为REQUEST/FORWARD/INCLUDE/ERROR
REQUEST:默认,普通请求,最常用
FORWARD:所拦截的资源是通过请求转发访问的
INCLUDE:所拦截的资源是通过页面包含访问的
ERROR:所拦截的资源通过异常机制访问的
</filter-mapping>
------------------------------------------------
2.4.Filter中的方法介绍
--------------------------------------------
init(FilterConfig filterConfig)
FilterConfig -- 代表当前Filter在web.xml中配置信息的对象
通过这一对象可以获取当前过滤器在web.xml配置的初始化参数
通过这一对象可以获取代表当前web应用的ServletContext对象
获取初始化参数:
getInitParameter(String name);
getInitParameterNames()
获取ServletContext对象
getServletContext();
--------------------------------------------
doFilter(request, response, FilterChian filterChian)
FilterChian -- 过滤器链
一个web资源可以被多个过滤器所拦截, 多个过滤器拦截的顺序是按照Filter在web.xml中配置的<filter-mapping>的顺序执行的.这多个过滤器按照拦截的顺序就组成了一个拦截器链, 用FilterChian表示.
如果一个过滤器处理完所拦截的请求后, 想要执行后面的拦截器, 则可以调用FilterChian上doFilter方法, 表示放行过滤器, 接着执行下一个节点
如果下一个节点仍然是过滤器, 则接着进行过滤, 执行的过程同上
如果没有后续的过滤器, 则执行真正的资源处理这次请求
--------------------------------------------
destroy()
略
===================================================
三.过滤器的应用
------------------------------------------------
3.1.全站乱码解决过滤器
(1).创建EncodingFilter过滤器类, 实现过滤器接口(Filter)
详细代码参考: EncodingFilter.java
(2).在web.xml中配置过滤器
<!-- 配置全站乱码解决过滤器 -->
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>cn.tedu.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
------------------------------------------------
3.2.自动登陆过滤器实现
(1).创建AutoLoginFilter过滤器类, 实现过滤器接口(Filter)
详细代码参考: AutoLoginFilter.java
(2).在web.xml中配置过滤器
<!-- 配置自动登陆过滤器 -->
<filter>
<filter-name>AutoLoginFilter</filter-name>
<filter-class>cn.tedu.filter.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AutoLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(3).在LoginServlet中, 实现30天自动登陆, 将用户名和密码保存进Cookie
if("true".equals(request.getParameter("autologin"))){
//实现30天自动登陆
Cookie cookie = new Cookie("autologin", username+":"+password);
cookie.setPath(request.getContextPath()+"/");
cookie.setMaxAge(3600*24*30);
response.addCookie(cookie);
}
===================================================
四.//案例--装饰者模式在乱码处理中的应用
public class EncodingFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("全站乱码解决过滤器初始化成功...");
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//1.解决响应正文乱码
response.setContentType("text/html;charset=utf-8");
//2.解决请求参数乱码 -- (利用装饰设计模式对request对象进行包装)
HttpServletRequest myReq = new MyHttpServletRequest((HttpServletRequest)request);
//3.放行过滤器
chain.doFilter(myReq, response);
}
public void destroy() {
}
}
class MyHttpServletRequest extends HttpServletRequestWrapper{
//将request对象保存在类的内部
private HttpServletRequest request;
//定义flag, 控制getParameterMap()方法中map的遍历次数
private boolean flag = true;
public MyHttpServletRequest(HttpServletRequest request) {
super(request);//这行代码千万不要省写!!!
this.request = request;
}
public String getParameter(String name) {
return getParameterValues(name) == null ? null : getParameterValues(name)[0];
}
public String[] getParameterValues(String name) {
return (String[]) getParameterMap().get(name);
}
public Map getParameterMap() {
try {
String method = request.getMethod();
if("POST".equals(method)){//--POST提交
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
}else if("GET".equals(method)){
//手动编解码解决乱码问题!
Map<String, String[]> map = request.getParameterMap();
if(flag){
for(Map.Entry<String, String[]> entry : map.entrySet()){
String[] vs = entry.getValue();
for(int i=0; i<vs.length; i++){
vs[i] = new String(vs[i].getBytes("iso8859-1"), "utf-8");
}
}
flag = false;
}
return map;
}else{
return request.getParameterMap();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
=================================================
五.装饰类详解
1. request继承结构
ServletRequest(接口)
|-- HttpServletRequest(接口)
|-- 匿名实现类(xxx) 实例: request对象
2. ServletRequestWrapper装饰类
request对象 --> 被装饰者
ServletRequestWrapper--> 装饰类
1.ServletRequestWrapper装饰类 和 被装饰者(request对象)所属的类(xxx)实现了同一个接口(ServletRequest)
2.提供了构造方法允许将被装饰者传入并保存在了类的内部
3.对于不想改造的方法直接调用已有对象上的方法, 对于想要改造的方法直接进行改造(没有对任何方法进行改造), 如:
3. HttpServletRequestWrapper装饰类
request对象 --> 被装饰者
HttpServletRequestWrapper -- 装饰类
HttpServletRequestWrapper类继承了ServletRequestWrapper装饰类类, 所以HttpServletRequestWrapper也是一个装饰类!!
HttpServletRequestWrapper类没有直接去包装request对象, 而是先将当前构造方法中的request对象传给父类(ServletRequestWrapper), 让父类进行包装, 再继承父类中包装后的方法。
而对于自身独有的方法, 自己再进行包装: 通过父类提供的方法(super.getRequest()) 获取 包装后的request对象, 并强制转型为 HttpServletRequest, 并通过提供 _getHttpServletRequest 方法, 方便在当前类的内部使用, 代码如下:
对于HttpServletRequestWrapper类中所有的方法, 直接调 super.getRequest() 对象 -- 即被父类包装后的request对象上的方法
也就是说, 对于HttpServletRequestWrapper装饰类, 是向将自己构造方法中的request对象传给父类(方便父类进行包装), 再通过super.getRequest(); 获取父类中包装的request对象(目的是保证自己和父类包装的是同一个request)
接下来对内部的方法进行包装, 即HttpServletRequestWrapper类中的方法分为两类: 第一类是通过父类继承过来的(父类对于这行方法已经进行包装), 第二类是自己独有的方法, 在自身类的内部进行包装!!
过滤器 & 装饰者模式