在JavaWeb系统的开发过程中,中文乱码是很头疼的问题,如何解决中文乱码成了一个很棘手的问题。

其实解决中文乱码问题的核心思想还是编码的转换,即把字符串从一种编码方式转换成另一种编码方式,如:把字符串从ISO-8859-1这种编码转换成UTF-8。

对于这种实现,我们经常地做法如下:

String value = "2121fafsdfsdfsdfsd中文";
value = new String(value.getBytes("iso8859-1"),"UTF-8");

这种解决方案如果要是在每个java类中去操作的话会很烦人的,万一需要变一种编码方式(当然这种情况极少出现,几乎不可能)的话,需要修改的地方太多,不便于后期的维护。要是能够采用一种统一的解决方案来解决这个问题的话,将会起到事半功倍的效果。

那么,我们该如何实现统一的解决方案呢?这时,过滤器就派上用场了。

思路就是我们可以做一个过滤器,对所有的请求进行统一的编码转换。实现方式如下:

方式一:使用包装类的形式解决

首先编写过滤器:

package com.ygp.filter;

import java.io.IOException;

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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

/**
 * @function:设置系统统一的编码方式为UTF-8,解决中文乱码问题
 * @author yankunpeng
 * @date 2015-1-27下午03:19:29
 * @mailto yan095650@163.com
 */
public class MyEncodingFilter implements Filter {
    private static final Logger log = Logger.getLogger(MyEncodingFilter.class);
    private String encoding;
    private FilterConfig filterConfig;

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    public void doFilter(ServletRequest req, ServletResponse rsp,
            FilterChain chain) throws IOException, ServletException {
        log.info("进入" + log.getName() + "的doFilter方法");
        log.info("开始执行过滤器MyEncodingFilter.............");
        log.info("encoding:" + encoding);
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) rsp;

        // 解决post中文乱码问题
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);
        response.setContentType("text/html;charset=" + encoding);

        // 解决GET中文乱码问题
        MyHttpServletRequestWrapper myRequest = new MyHttpServletRequestWrapper(
                request);
        // 传递给目标servlet或jsp的实际上时包装器对象的引用,而不是原始的HttpServletRequest对象
        chain.doFilter(myRequest, response);

        log.info("离开" + log.getName() + "的doFilter方法");
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("进入" + log.getName() + "的init方法");
        log.info("开始初始化过滤器MyEncodingFilter.............");
        this.filterConfig = filterConfig;
        String encoding = filterConfig.getServletContext().getInitParameter(
                "encoding");
        if (encoding != null && encoding.trim().length() != 0) {
            this.encoding = encoding;
        }

        log.info("encoding:" + encoding);
        log.info("离开" + log.getName() + "的init方法");
    }

    public void destroy() {
        log.info("进入" + log.getName() + "的destroy方法");
        log.info("开始销毁过滤器MyEncodingFilter.............");
        System.out.println(filterConfig.getFilterName() + "被销毁");
        encoding = null;
        filterConfig = null;
        log.info("离开" + log.getName() + "的destroy方法");
    }
}

实现我们的包装类:

package com.ygp.filter;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.log4j.Logger;

/**
 * @function:设置系统统一的编码方式为UTF-8,解决中文乱码问题
 * @author yankunpeng
 * @date 2015-1-27下午03:19:29
 * @mailto yan095650@163.com
 */
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private static final Logger log = Logger
            .getLogger(MyHttpServletRequestWrapper.class);

    private String encoding = "UTF-8";
    private HttpServletRequest request;

    public MyHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    /**
     * 获得被装饰对象的引用和采用的字符编码
     * 
     * @param request
     * @param encoding
     */
    public MyHttpServletRequestWrapper(HttpServletRequest request,
            String encoding) {
        super(request);
        this.encoding = encoding;
        this.request = request;
    }

    /**
     * 实际上就是调用被包装的请求对象的getParameter方法获得参数,然后再进行编码转换
     */
    public String getParameter(String name) {
        String value = this.request.getParameter(name);
        value = ((value == null) ? null : convert(value));
        return value;
    }

    public String convert(String parameter) {
        log.info("编码转换之前:" + parameter);
        String value = "";
        try {
            if (this.request.getMethod().equalsIgnoreCase("GET")) {
                value = new String(parameter.trim().getBytes("ISO-8859-1"),
                        encoding);
            } else {
                value = parameter;
            }
        } catch (UnsupportedEncodingException e) {
            log.info(e);
        }

        return value;
    }

    @Override
    public String[] getParameterValues(String name) {
        String[] values = this.request.getParameterValues(name);
        String[] newValues = new String[values.length];
        // 判断是否是GET
        if (request.getMethod().equalsIgnoreCase("GET")) {
            for (int i = 0; i < values.length; i++) {
                try {
                    if (values[i] != null) {
                        newValues[i] = new String(values[i].getBytes(), request
                                .getCharacterEncoding());
                    } else {
                        newValues[i] = null;
                    }
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
            return newValues;
        } else {
            return values;
        }
    }

    // 重构方法
    @SuppressWarnings("unchecked")
    @Override
    /**
     * 此方法是struts2的模型驱动需要使用的方法,所以需要重写
     */
    public Map<String, String[]> getParameterMap() {
        try {
            if (!this.request.getMethod().equals("GET")) {// 判断是否是get请求方式
                return this.request.getParameterMap();
            }

            Map<String, String[]> map = this.request.getParameterMap(); // 接受客户端的数据
            Map<String, String[]> newmap = new HashMap<String, String[]>();
            for (Map.Entry<String, String[]> entry : map.entrySet()) {
                String name = entry.getKey();
                String values[] = entry.getValue();

                if (values == null) {
                    newmap.put(name, new String[] {});
                    continue;
                }
                String newvalues[] = new String[values.length];
                for (int i = 0; i < values.length; i++) {
                    String value = values[i];
                    value = new String(value.getBytes("iso8859-1"),
                            this.request.getCharacterEncoding());
                    newvalues[i] = value; // 解决乱码后封装到Map中
                }

                newmap.put(name, newvalues);
            }

            return newmap;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

最后是我们的配置文件web.xml:

<!-- 设置系统的全局变量 :将采用的字符编码配置成应用初始化参数而不是过滤器私有的初始化参数是因为在JSP和其他地方也可能需要使用 -->
    <context-param>
        <description>全局编码 </description>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </context-param>
    <!-- 设置统一编码方式为UTF-8 -->
    <filter>
        <filter-name>myEncodingFilter</filter-name>
        <filter-class>com.ygp.filter.MyEncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>myEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

方式二:使用动态代理的方式解决

首先使用动态代理实现我们的过滤器:

package com.ygp.filter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

public class CharacterEncodingFilter implements Filter {
    private static final Logger log = Logger
            .getLogger(CharacterEncodingFilter.class);
    private String encoding = "UTF-8";// 这里定义的事默认值,如果web.xml中没有配置,则取这个默认值,否则取配置的值
    private String old_encoding = "ISO-8859-1";
    private FilterConfig filterConfig;

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        log.info("进入" + log.getName() + "的doFilter方法");
        log.info("开始执行过滤器CharacterEncodingFilter.............");
        log.info("encoding:" + encoding);
        final HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        // 解决post中文乱码问题
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);
        response.setContentType("text/html;charset=" + encoding);

        // 解决get请求的中文乱码.放行将代理对象带过去,将会拦截对getParameter,getParameterValues,getParameterMap的访问,转正常了,再返回给浏览器
        chain.doFilter((ServletRequest) Proxy.newProxyInstance(this.getClass()
                .getClassLoader(), request.getClass().getInterfaces(),
                new InvocationHandler() {

                    @SuppressWarnings("unchecked")
                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        String methodname = method.getName(); // 拿到当前的方法

                        if (methodname.equals("getParameter")) {
                            // 执行request.getParameter获取结果
                            String value = (String) method
                                    .invoke(request, args);
                            // 判断是否是GET
                            if (request.getMethod().equalsIgnoreCase("GET")) {
                                try {
                                    if (value != null) {
                                        value = new String(value
                                                .getBytes(old_encoding),
                                                request.getCharacterEncoding());// 转换编码返回
                                    }
                                } catch (UnsupportedEncodingException e) {
                                    e.printStackTrace();
                                }
                            }

                            return value;
                        }

                        if (methodname.equals("getParameterValues")) {
                            String[] values = (String[]) method.invoke(request,
                                    args); // 执行request.getParameterValues获取结果
                            String[] newValues = new String[values.length];
                            // 判断是否是GET
                            if (request.getMethod().equalsIgnoreCase("GET")) {
                                for (int i = 0; i < values.length; i++) {
                                    try {
                                        if (values[i] != null) {
                                            newValues[i] = new String(values[i]
                                                    .getBytes(), request
                                                    .getCharacterEncoding());
                                        } else {
                                            newValues[i] = null;
                                        }
                                    } catch (UnsupportedEncodingException e) {
                                        e.printStackTrace();
                                    }
                                }
                                return newValues;
                            } else {
                                return values;
                            }
                        }

                        if (methodname.equals("getParameterMap")) {
                            try {
                                // 执行request.getParameterMap获取结果
                                Map<String, String[]> map = (Map<String, String[]>) method
                                        .invoke(request, args);
                                Map<String, String[]> newmap = new HashMap<String, String[]>();

                                // 判断是否是GET
                                if (request.getMethod().equalsIgnoreCase("GET")) {
                                    for (Map.Entry<String, String[]> entry : map
                                            .entrySet()) {
                                        String name = entry.getKey();
                                        String values[] = entry.getValue();

                                        if (values == null) {
                                            newmap.put(name, new String[] {});
                                            continue;
                                        }
                                        String newvalues[] = new String[values.length];
                                        for (int i = 0; i < values.length; i++) {
                                            String value = values[i];
                                            value = new String(
                                                    value
                                                            .getBytes(old_encoding),
                                                    request
                                                            .getCharacterEncoding());
                                            newvalues[i] = value; // 解决乱码后封装到Map中
                                        }

                                        newmap.put(name, newvalues);
                                    }

                                    return newmap;
                                } else {
                                    return map;
                                }
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        }

                        // 交给request执行请求
                        return method.invoke(request, args);
                    }
                }), response);

        log.info("离开" + log.getName() + "的doFilter方法");
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("进入" + log.getName() + "的init方法");
        log.info("开始初始化过滤器CharacterEncodingFilter.............");
        this.filterConfig = filterConfig;
        String encoding = filterConfig.getServletContext().getInitParameter(
                "encoding");
        if (encoding != null && encoding.trim().length() != 0) {
            this.encoding = encoding;
        }

        log.info("encoding:" + encoding);
        log.info("离开" + log.getName() + "的init方法");
    }

    public void destroy() {
        log.info("进入" + log.getName() + "的destroy方法");
        log.info("开始销毁过滤器CharacterEncodingFilter.............");
        System.out.println(filterConfig.getFilterName() + "被销毁");
        encoding = "UTF-8";
        filterConfig = null;
        log.info("离开" + log.getName() + "的destroy方法");
    }
}

最后是配置文件web.xml配置:

<!-- 设置系统的全局变量 :将采用的字符编码配置成应用初始化参数而不是过滤器私有的初始化参数是因为在JSP和其他地方也可能需要使用 -->
    <context-param>
        <description>全局编码 </description>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </context-param>
    <!-- 设置统一编码方式为UTF-8 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>com.ygp.filter.CharacterEncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

使用包装类和动态代理的方式均可以实现我们的功能。