Servlet API包含4个可修饰的类,用于改变Servlet Request以及Servlet Response。这种修饰允许修改 ServletRequest以及ServletResponse或者HTTP中的等价 类(即HttpServletRequest和HttpServletResponse)中的 任务方法。这种修饰遵循Decorator模式或者Wrapper模 式,因此在使用修饰前,需要了解一下该模式的内容。
一.Decorator 模式( 装饰器模式)
Decorator模式或者Wrapper模式允许修饰或者封装 (在字面意义中,即修改行为)一个对象,即使你没有 该对象的源代码或者该对象标识为final。
Decorator模式适用于无法继承该类(例如,对象的 实现类使用final标识)或者无法创建该类的实例,但可 以从另外的系统中可以取得该类的实现时。例如, Servlet容器方法。只有一种方法可以修改ServletRequest 或者ServletResponse行为,即在另外的对象中封装该实 例。唯一的限制是,修饰对象必须继承一个接口,然后 实现接口以封装这些方法。
类图说明了一个Component接口以及它 的实现类ComponentImpl。Component接口定义了A的方 法。为了修饰ComponentImpl的实例,需要创建一个 Decorator类,并实现Component的接口,然后在子类中 扩展Decorator的新行为。在类图中DecoratorA就是 Decorator的一个子类。每个Decorator实例需要包含 Component的一个实例。Decorator类代码如下(注意在 构建函数中获取了Component的实例,这意味着创建 Decorator对象只能传入Component的实例):
public class Decorator implements Component { private Component decorated; // constructor takes a Component implementation public Decorator(Component component) { this.decorated = component; } // undecorated method @Override public void methodA(args) { decorated.methodA(args); } // decorated method @Override public void methodB(args) { decorated.methodB(args) } }
在Decorator类中,有修饰的方法就是可能在子类中 需要修改行为的方法,在子类中不需要修饰的方法可以 不需要实现。所有的方法,无论是否需要修饰,都叫作 Component中的配对方法。Decorator是一个非常简单的 类,便于提供每个方法的默认实现。修改行为在它的子 类中。
需要牢记一点,Decorator类及被修饰对象的类需要 实现相同的接口。为了实现Decorator,可以在Decorator 中封装修饰对象,并把Decorator作为Component的一个 实现。任何Component的实现都可以在Decorator中注 入。事实上,你可以把一个修饰的对象传入另一个修饰 的对象,以实现双重的修饰。
二. Servlet封装类
Servlet API源自于4个实现类,它很少被使用,但 是十分强大:ServletRequestWrapper、 ServletResponseWrapper以及 HttpServletRequestWrapper、 HttpServletResponseWrapper。
ServletRequestWrapper(或者其他3个Wrapper类) 非常便于使用,因为它提供了每个方法的默认实现:即 ServletRequest封闭的配置方法。通过继承 ServletRequestWrapper,只需要实现你需要变更的方法 就可以了。如果不用ServletRequestWrapper,则需要继 承ServletRequest并实现ServletRequest中所有的方法。
图所示为Decorator模式中 ServletRequestWrapper的类图。Servlet容器在每次 Servlet服务调用时创建ServletRequest、ContainerImpl。 直接扩展ServletRequestWrapper就可以修饰 ServletRequest了。
三. 示例:AutoCorrect Filter
在Web应用中,用户经常在单词的前面或者后面输 入空格,更有甚者在单词之间也加入空格。是否很想在 应用的每个Servlet中,把多余的空格删除掉呢?本节的 AutoCorrect Filter可以帮助你搞定它。该Filter包含了 HttpServletRequestWrapper子类 AutoCorrectHttpServletRequestWrapper,并重写了返回 参数值的方法:getParameter、getParameterValues及 getParameterMap。代码如下所示。
AutoCorrectFilter 类
package filter; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; // urlPatterns={"/*"} 不能过滤链接 @WebFilter(filterName = "AutoCorrectFilter", urlPatterns = { "*" }) public class AutoCorrectFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; AutoCorrectHttpServletRequestWrapper wrapper = new AutoCorrectHttpServletRequestWrapper(httpServletRequest); filterChain.doFilter(wrapper, response); } class AutoCorrectHttpServletRequestWrapper extends HttpServletRequestWrapper { private HttpServletRequest httpServletRequest; public AutoCorrectHttpServletRequestWrapper(HttpServletRequest httpServletRequest) { super(httpServletRequest); this.httpServletRequest = httpServletRequest; } @Override public String getParameter(String name) { return autoCorrect(httpServletRequest.getParameter(name)); } @Override public String[] getParameterValues(String name) { return autoCorrect(httpServletRequest.getParameterValues(name)); } //用匿名内部类 重写 getParameterMap() 方法 @Override public Map<String, String[]> getParameterMap() { // 得到request的 parameterMap final Map<String, String[]> parameterMap = httpServletRequest.getParameterMap(); // 重写 parameterMap Map<String, String[]> newMap = new Map<String, String[]>() { @Override public int size() { return parameterMap.size(); } @Override public boolean isEmpty() { return parameterMap.isEmpty(); } @Override public boolean containsKey(Object key) { return parameterMap.containsKey(key); } @Override public boolean containsValue(Object value) { return parameterMap.containsValue(value); } @Override public String[] get(Object key) { return autoCorrect(parameterMap.get(key)); } @Override public void clear() { // this will throw an IllegalStateException // but let the user get the original // exception parameterMap.clear(); } @Override public Set<String> keySet() { return parameterMap.keySet(); } @Override public Collection<String[]> values() { return autoCorrect(parameterMap.values()); } @Override public Set<Map.Entry<String, String[]>> entrySet() { return autoCorrect(parameterMap.entrySet()); } @Override public String[] put(String key, String[] value) { // this will throw an IllegalStateException // but let the user get the original // exception return parameterMap.put(key, value); } @Override public void putAll(Map<? extends String, ? extends String[]> map) { // this will throw an IllegalStateException // but let // the user get the original exception parameterMap.putAll(map); } @Override public String[] remove(Object key) { // this will throw an IllegalStateException, // but let // the user get the original exception return parameterMap.remove(key); } }; //返回一个新的map return newMap; } } private String autoCorrect(String value) { if (value == null) { return null; } // 去除String头尾空格 value = value.trim(); int length = value.length(); StringBuilder temp = new StringBuilder(); boolean lastCharWasSpace = false; for (int i = 0; i < length; i++) { char c = value.charAt(i); // 如果是空格 if (c == ' ') { // 如果是空格且是最后一个字符 就添加到temp if (!lastCharWasSpace) { temp.append(c); } lastCharWasSpace = true; } else { // 不是空格的字符直接添加到temp temp.append(c); lastCharWasSpace = false; } } return temp.toString(); } private String[] autoCorrect(String[] values) { if (values != null) { int length = values.length; for (int i = 0; i < length; i++) { values[i] = autoCorrect(values[i]); } return values; } return null; } private Collection<String[]> autoCorrect(Collection<String[]> valueCollection) { Collection<String[]> newCollection = new ArrayList<String[]>(); for (String[] values : valueCollection) { newCollection.add(autoCorrect(values)); } return newCollection; } private Set<Map.Entry<String, String[]>> autoCorrect(Set<Map.Entry<String, String[]>> entrySet) { Set<Map.Entry<String, String[]>> newSet = new HashSet<Map.Entry<String, String[]>>(); for (final Map.Entry<String, String[]> entry : entrySet) { Map.Entry<String, String[]> newEntry = new Map.Entry<String, String[]>() { @Override public String getKey() { return entry.getKey(); } @Override public String[] getValue() { return autoCorrect(entry.getValue()); } @Override public String[] setValue(String[] value) { return entry.setValue(value); } }; newSet.add(newEntry); } return newSet; } }
这个Filter的doFilter方法非常简单:创建 ServletRequest的修饰实现,然后,把修饰类传给 doFilter
HttpServletRequest httpServletRequest = (HttpServletRequest) request; AutoCorrectHttpServletRequestWrapper wrapper = new AutoCorrectHttpServletRequestWrapper( httpServletRequest); filterChain.doFilter(wrapper, response);
在这个Filter背后的任何Servlet获得的 HttpServletRequest都将被AutoCorrectHttp ServletRequestWrapper 所封装。这个封装类很长,但很 好理解。简单地说,就是它把所有获取参数的响应都调 用了一下autoCorrect方法:
test1.jsp页面
<!DOCTYPE html> <html> <head> <title>User Form</title> </head> <body> <form action="test2.jsp" method="post"> <table> <tr> <td>Name:</td> <td><input name="name"/></td> </tr> <tr> <td>Address:</td> <td><input name="address"/></td> </tr> <tr> <td colspan="2"> <input type="submit" value="Login"/> </td> </tr> </table> </form> </body> </html>
test2.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> <!DOCTYPE html> <html> <head> <title>Form Values</title> </head> <body> <table> <tr> <td>Name:</td> <td>${param.name} (length:${fn:length(param.name)})</td> </tr> <tr> <td>Address:</td> <td>${param.address} (length:${fn:length(param.address)})</td> </tr> </table> </body> </html>
输入一个带空格的单词,无论是前面、后面,还是 在单词之间,然后点击提交。接下来,在显示器上你将 看到这些输入单词都被修正过来