原文:http://javatar.iteye.com/blog/40188
字符串过滤,是比较常用的功能,我的当前项目也有用到。
如过滤User输入的Html,Js代码等,
由于过滤需求是可能变动的,
如客户又要你过滤一些脏词或者为内容中的url自动加上超链接等。
考虑“开-闭”(OCP)原则,
我决定使用装饰器(Decorator)模式。
首先定义Decorator接口:
- package com.sanook.hompy.util.filter;
- public interface StringFilter {
- public void setNextStringFilter(StringFilter stringFilter); //关联下一装饰器
- public String filter(String source); //处理过滤
- }
然后用模板方法(Template Method)模式实现一个抽象的过滤器:
这样可以将相同的实现部分抽象出来。
- package com.sanook.hompy.util.filter;
- public abstract class AbstractStringFilter implements StringFilter {
- private StringFilter stringFilter;
- public void setNextStringFilter(StringFilter stringFilter) {
- this.stringFilter = stringFilter;
- }
- public String filter(String source) {
- String target = doFilter(source);
- if (stringFilter == null) {
- return target;
- }
- return stringFilter.filter(target);
- }
- // 模板抽象方法,传入要处理的string,返回处理完的string
- // 遵循模板方法doXXX命名方式
- public abstract String doFilter(String source);
- }
空的实现:
- package com.sanook.hompy.util.filter;
- public class EmptyFilter extends AbstractStringFilter {
- public String doFilter(String source) {
- return source;
- }
- }
下面实现该接口的Html过滤:
- package com.sanook.hompy.util.filter;
- import org.apache.commons.lang.StringUtils;
- public class HtmlFilter extends AbstractStringFilter {
- public String doFilter(String source) {
- source = StringUtils.replace(source, "<", "& lt;");
- source = StringUtils.replace(source, ">", "& gt;");
- source = StringUtils.replace(source, "&", "& amp;");
- source = StringUtils.replace(source, " ", "& nbsp;");
- source = StringUtils.replace(source, "\"", "& #0034;");
- source = StringUtils.replace(source, "\'", "& #0039;");
- return source;
- }
- }
由于Decorator是嵌套结构(注:这里只用了前(before)装饰,所以看起来有点像链结构,如有必要,也可以加上后(after)装饰),
它的调用关系需要组装,所以应该用建造者(Builder)模式或简单工厂模式。
这里使用简单工厂模式,工厂的获取用单例(Singleton)模式返回
- package com.sanook.hompy.util.filter;
- import java.util.HashMap;
- import java.util.Map;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import com.sanook.hompy.util.manager.ConfigureManager;
- public class StringFilterFactory {
- private static final Log log = LogFactory.getLog(StringFilterFactory.class);
- private static final StringFilterFactory stringFilterFactory = new StringFilterFactory();
- private Map filterMap = new HashMap();
- private StringFilterFactory() {
- //ConfigureManager是Hompy项目统一读取配置的类,以多例(Multi-Singleton)模式实现
- filterMap = ConfigureManager.getInstance("filter").getMap();
- }
- public static StringFilterFactory getInstance() {
- return stringFilterFactory;
- }
- //通过一个以逗号分割的过滤器引用名串,获取过滤器
- public StringFilter getStringFilterChain(String chain) {
- if (chain == null || chain.length() == 0) {
- return new EmptyFilter();
- }
- if ("all".equalsIgnoreCase(chain)) {
- return getAllStringFilterChain();
- }
- String[] filters = chain.split("\\,");
- return getStringFilterChain(filters);
- }
- public StringFilter getAllStringFilterChain() {
- String[] filters = (String[]) filterMap.values().toArray();
- return getStringFilterChain(filters);
- }
- public StringFilter getStringFilterChain(String[] filters) {
- if (filters == null || filters.length == 0) {
- return new EmptyFilter();
- }
- StringFilter[] stringFilters = new StringFilter[filters.length];
- for (int i = filters.length - 1; i >= 0; i--) {
- stringFilters[i] = getStringFilter(filters[i]);
- if (i != filters.length - 1) {
- stringFilters[i].setNextStringFilter(stringFilters[i + 1]);
- } else {
- stringFilters[i].setNextStringFilter(null);
- }
- }
- return stringFilters[0];
- }
- public StringFilter getStringFilter(String key) {
- if (key != null) {
- try {
- //通过类名反射得到过滤器的实例
- Class clazz = Class.forName((String) filterMap.get(key));
- StringFilter stringFilter = (StringFilter) clazz.newInstance();
- return stringFilter;
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- log.warn(e);
- } catch (InstantiationException e) {
- e.printStackTrace();
- log.warn(e);
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- log.warn(e);
- }
- }
- return new EmptyFilter();
- }
- }
配置文件filter.properties如下:
- html=com.sanook.hompy.util.filter.HtmlFilter
- url=com.sanook.hompy.util.filter.UrlFilter
- js=com.sanook.hompy.util.filter.JavaScriptFilter
- dirty=com.sanook.hompy.util.filter.DirtyWordFilter
- quote=com.sanook.hompy.util.filter.QuotationMarkFilter
- line=com.sanook.hompy.util.filter.NewLineFilter
- lower=com.sanook.hompy.util.filter.LowerFilter
这些配置将通过ConfigureManager读到filterMap中,
其中key作为chain的引用名,value为过滤器对象名。
调用方式:
- String chain = "html,js,dirty";
- StringFilter stringFilter = StringFilterFactory.getInstance().getStringFilterChain(chain);
- String source = "<b>aaaa</b>";
- String result = stringFilter.filter(source);
现在如果你要扩展一个过滤器,只要继承AbstractStringFilter,实现doFilter(String source)方法,
在filter.properties加入其引用名即可。上面的配置示例中就是Hompy项目用到的一些过滤器。
Hompy项目以JSP作为View层,而StringFiler是属于展示逻辑,应由View层控制,所以,我使用了自定义标签。
- package com.sanook.hompy.servlet.tag;
- import com.sanook.hompy.util.filter.StringFilter;
- import com.sanook.hompy.util.filter.StringFilterFactory;
- public class FilterTag extends BodyOutTag {
- private static final long serialVersionUID = 1L;
- private String chain;
- public void setChain(String chain) {
- this.chain = chain;
- }
- public String doBody(String body) {
- StringFilter stringFilter = StringFilterFactory.getInstance()
- .getStringFilterChain(chain);
- return stringFilter.filter(body);
- }
- }
其父类BodyOutTag是一个抽象类
- package com.sanook.hompy.presentation.tag;
- import javax.servlet.jsp.JspException;
- import javax.servlet.jsp.JspTagException;
- import javax.servlet.jsp.tagext.BodyTagSupport;
- public abstract class BodyOutTag extends BodyTagSupport {
- private String body;
- public BodyOutTag() {
- super();
- init();
- }
- private void init() {
- body = null;
- }
- public void setBody(String body) {
- this.body = body;
- }
- public int doStartTag() throws JspException {
- return EVAL_BODY_BUFFERED;
- }
- public int doEndTag() throws JspException {
- if (body == null) {
- if (bodyContent != null && bodyContent.getString() != null) {
- body = bodyContent.getString().trim();
- } else {
- body = "";
- }
- }
- /*如果继承SimpleTagSupport,则用:
- if (body == null) {
- body = "";
- JspFragment body = getJspBody();
- if (body != null) {
- StringWriter writer = new StringWriter();
- body.invoke(writer);
- body = writer.toString();
- }
- }*/
- body = doBody(body);
- try {
- pageContext.getOut().print(body == null ? "" : body);
- } catch (java.io.IOException ex) {
- throw new JspTagException(ex.getMessage());
- }
- body = null;
- return EVAL_PAGE;
- }
- public void release() {
- super.release();
- init();
- }
- public abstract String doBody(String body);
- }
配置/WEB-INF/tld/hompy-string.tld,
(我将其归纳在string处理namespace内)
- <?xml version="1.0" encoding="UTF-8" ?>
- <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">
- <description>hompy string tag library</description>
- <display-name>string</display-name>
- <tlib-version>1.0</tlib-version>
- <short-name>s</short-name>
- <uri>http://hompy.sanook.com/tag/string</uri>
- <tag>
- <description></description>
- <name>filter</name>
- <tag-class>com.sanook.hompy.servlet.tag.FilterTag</tag-class>
- <body-content>JSP</body-content>
- <attribute>
- <description>body</description>
- <name>body</name>
- <required>false</required>
- <rtexprvalue>true</rtexprvalue>
- <type>java.lang.String</type>
- </attribute>
- <attribute>
- <description>filter chain key, separator is ,</description>
- <name>chain</name>
- <required>true</required>
- <rtexprvalue>true</rtexprvalue>
- </attribute>
- </tag>
- </taglib>
在web.xml的适当位置加入:
- <taglib>
- <taglib-uri>hompy-string</taglib-uri>
- <taglib-location>/WEB-INF/tld/hompy-string.tld</taglib-location>
- </taglib>
在jsp页面中使用如下:
- <%@ page language="java" contentType="text/html" pageEncoding="UTF-8" isELIgnored="false"%>
- <%@ taglib uri="hompy-string" prefix="s"%>
- <html>
- <body>
- Test Filter: <s:filter chain="html,js,dirty">${picture.title}</s:filter>
- </body>
- </html>