动态代理踩坑:java.lang.IllegalStateException: No modifications are allowed to a locked ParameterMap

最近学习JavaWeb的Filter过滤器,有一个这样的需求:用Filter过滤掉提交数据中指定的敏感字符,把敏感字符用***代替。

这个需求需要使用到动态代理,基本思路是对request中获取请求参数的方法进行增强,在增强方法中执行request对象的getParameter系列方法,对获取到的参数值进行判断是否包含敏感字符,如果包含,就将敏感字符替换为"***"后返回。getParameter和getParameterValues这两个方法都能成功增强,但就是增强getParameterMap这个方法会报以下运行时异常:

java.lang.IllegalStateException: No modifications are allowed to a locked ParameterMap

经过查阅资料,了解到这是由于 javax.servlet.ServletRequest中的getParameterMap() 方法返回的是一个不可变(immutable)的对象。

什么是Immutable class?

创建一个Immutable 类需要满足以下条件

1. 用final 声明类,使类不可以被继承

2. 所有的属性字段都用private 修饰,以至于不能直接访问属性

3. 不提供setter 方法

4. 使所有的mutable fields 用final 修饰, 以至于这个值只能被分配一次

5. 用构造函数 深度copy 和初始化所有的字段

6. 在Getter方法里通过clone 对象,相当于返回一个实际对象的引用。

从上述的immutable class中的第四条可以得知,在第一次获取表单数据时,ParameterMap已经put(K1,V1)。当我们检测到敏感字符,将敏感字符替换为"***"生成新的字符串V2后,再一次put(K1,V2)试图将V1的值替换为V2的值,此时就会发生以上异常。 因为这个K1的值已经被分配过一次,但是被final修饰,导致再次分配的时候就会报这个错误。

解决方法是,在增强方法中执行getParameterMap方法时,需要自己new一个新的map,将所有的数据重新封装到这个新的map中,这样就可以在新的map中对键值对进行任意修改了。最终将修改后的新的map返回。

原来的语句:

Map<String,String[]> map = (Map) method.invoke(req, args);

修改后的语句:

Map<String,String[]> map = new HashMap<String,String[]>((Map) method.invoke(req, args));

AnonyEast

一个爱折腾的技术萌新

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐