微信号:tomcat0000

介绍:由从事应用服务器核心研发的工程师维护.文章深入Tomcat源码,分析应用服务器的实现细节,工作原理.以及与之相关的技术,使用技巧,工作实战等.起于Tomcat,但不止于此.同时会分享JVM、并发等,内容多为原创...

Tomcat自带的设置编码Filter

2016-03-26 23:59 侯树成


我们在前面的文章里曾写过Web应用中乱码产生的原因和处理方式,旧文回顾:深度揭秘乱码问题背后的原因及解决方式


其中我们提到可以通过Filter的方式来设置请求和响应的encoding,来解决乱码问题。

在Tomcat的org.apache.catalina.filters包中,有一系列Tomcat自带的Filter,可以处理不同的问题场景。今天我们来看一下,Tomcat自带的设置encoding的Filter,Tomcat开发者在实现这个功能的采用的方式。


1. SetCharacterEncodingFilter


    public void doFilter(ServletRequest request, ServletResponse response,

                         FilterChain chain) {

        // Conditionally select and set the character encoding to be used

        if (ignore || (request.getCharacterEncoding() == null)) {

            String characterEncoding = selectEncoding(request);

            if (characterEncoding != null) {

                request.setCharacterEncoding(characterEncoding);

            }

        }

        chain.doFilter(request, response);

    }


Filter的doFilter方法如上所示,在这个类中,除了encoding这个参数外,还提供了一个ignore参数。该参数是一个开关,主要用于决定是否要忽略客户端请求中指定的encoding,如果为true,我们看到流程会直接进行selectEncoding操作。在有特定的需要时,可以继承该Filter并重写其selectEncoding方法实现,例如根据请求头中的Accept-Language项进行对应的encoding设置,或者根据session中特定的标识进行设置。




附加参数的处理


我们知道,对于Filter或Servlet等,我们都可以在web.xml中为其指定初始化参数,我们称之为initParameter。对于我们自己的应用,可能已经定义好了要传入的参数,直接在应用内解析传入的值即可。


而Tomcat内部为了在多个Filter中支持多种形式的自定义initParameter,特意定义了一个工具类用于解析传入的参数。


我们上面提到的Filter,其继承自FilterBase这样一个基类,而基类中主要用于进行初始化参数的设置

public void init(FilterConfig filterConfig) throws ServletException {

        Enumeration<String> paramNames = filterConfig.getInitParameterNames();

        while (paramNames.hasMoreElements()) {

            String paramName = paramNames.nextElement();

            if (!IntrospectionUtils.setProperty(this, paramName,

                    filterConfig.getInitParameter(paramName))) {

                String msg = sm.getString("filterbase.noSuchProperty",

                        paramName, this.getClass().getName());

                if (isConfigProblemFatal()) {

                    throw new ServletException(msg);

                } else {

                    getLogger().warn(msg); }} }


这个工具类即为上面标红的IntrospectionUtils


Utils中主要使用反射进行参数值的设置,即反射调用对应参数的setter进行赋值。我提到这个工具类,主要目的并不是想说反射,而是其实现的方式可以进行参考,以及对于边界条件的考虑很周全。


例如,拿到class对应的Method之后,会判断其参数的个数,类型等。

       // First, the ideal case - a setFoo( String ) method

            for (int i = 0; i < methods.length; i++) {

                Class<?> paramT[] = methods[i].getParameterTypes();

                if (setter.equals(methods[i].getName()) && paramT.length == 1

                        && "java.lang.String".equals(paramT[0].getName())) {

                    methods[i].invoke(o, new Object[] { value });

                    return true;

                }

            }


在考虑其它类型时,除基础类型外,还考虑到了包装类

if (setter.equals(methods[i].getName())

                        && methods[i].getParameterTypes().length == 1) {


                    // match - find the type and invoke it

                    Class<?> paramType = methods[i].getParameterTypes()[0];

                    Object params[] = new Object[1];


                    // Try a setFoo ( int )

                    if ("java.lang.Integer".equals(paramType.getName())

                            || "int".equals(paramType.getName())) {

                        try {

                            params[0] = new Integer(value);

                        } catch (NumberFormatException ex) {

                            ok = false;

                        }

                    // Try a setFoo ( long )

                    }else if ("java.lang.Long".equals(paramType.getName())

                                || "long".equals(paramType.getName())) {

                            try {

                                params[0] = new Long(value);

                            } catch (NumberFormatException ex) {

                                ok = false;

                            }


                        // Try a setFoo ( boolean )

                    } else if ("java.lang.Boolean".equals(paramType.getName())

                            || "boolean".equals(paramType.getName())) {

                        params[0] = Boolean.valueOf(value);

}



对于参数的getter,除了getXXX,还想到了isXXX。

public static Object getProperty(Object o, String name) {

        String getter = "get" + capitalize(name);

        String isGetter = "is" + capitalize(name);



这着实值得我们参考借鉴!






Tomcat那些事儿

本公众号由从事应用服务器核心研发的工程师维护。文章深入Tomcat源码,分析应用服务器的实现细节,工作原理及与之相关的技术,使用技巧,工作实战等。起于Tomcat但不止于此。同时会分享并发、JVM等,内容多为原创,欢迎关注。


扫描或长按下方二维码,即可关注!




 
Tomcat那些事儿 更多文章 谁是Tomcat? 干嘛的? Tomcat 与Apache/Nginx有啥区别? 透过Tomcat配置认识其内部组件 如何加入Tomcat邮件组和开发者对话? 你可能不知道的几个java小工具
猜您喜欢 这一次为自己疯狂 微信支付成功后推荐关注栏规则---20160406最新 React:来自Facebook颠覆式前端UI开发框架 阿里巴巴技术论坛邀你共享技术盛宴 Android运行时Crash自动恢复框架:Recovery