Java中如何将ResourceBundle对象存储到Session中
ResourceBundle存Session序列化警告修复方案
警告原因
Java EE规范要求HttpSession中存储的所有属性必须实现Serializable接口,触发会话持久化(集群部署会话同步、服务器停机会话落盘等场景)时会直接抛出NotSerializableException异常。你当前功能正常只是因为还没触发这类会话持久化逻辑,生产环境存在隐忧。
最优解决方案:不直接存储ResourceBundle实例
你完全不需要把整个ResourceBundle对象存入Session,JDK本身对ResourceBundle有全局缓存,重复调用ResourceBundle.getBundle()不会有额外性能损耗。你只需要存入构造ResourceBundle需要的两个可序列化参数Locale和basename即可,修改逻辑如下:
- 修改
BundleHandler代码,不再存ResourceBundle,仅存可序列化的basename到Session:
public class BundleHandler extends SimpleTagSupport { private String basename; public void setBasename(String basename) { this.basename = basename; } @Override public void doTag() { PageContext pageContext = (PageContext)getJspContext(); HttpSession session = pageContext.getSession(); session.setAttribute("bundleBasename", basename); } }
- 调整
localeValue、LValueParam两个标签的取值逻辑,每次读取国际化值时实时获取ResourceBundle:
以localeValue标签为例,修改后代码如下:
public class LocalValueHandler extends SimpleTagSupport { private String key; public void setKey(String key) { this.key = key; } @Override public void doTag() throws Exception { PageContext pageContext = (PageContext) getJspContext(); HttpSession session = pageContext.getSession(); Locale locale = (Locale) session.getAttribute("tagLocale"); String basename = (String) session.getAttribute("bundleBasename"); // 实时获取ResourceBundle,JDK内置缓存无性能损耗 ResourceBundle bundle = ResourceBundle.getBundle(basename, locale); String value = bundle.getString(key); getJspContext().getOut().write(value); } }
该方案完全规避了非序列化对象存Session的问题,无性能损失,兼容性最好。
备选方案:封装可序列化的资源包实例
如果确实需要预加载资源包存入Session,可以自己实现一个可序列化的包装类,把ResourceBundle的内容转存到序列化的Map中:
import java.io.Serializable; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; public class SerializableResourceBundle implements Serializable { private final Map<String, String> resourceMap = new HashMap<>(); public SerializableResourceBundle(ResourceBundle originalBundle) { Enumeration<String> keys = originalBundle.getKeys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); resourceMap.put(key, originalBundle.getString(key)); } } public String getString(String key) { return resourceMap.get(key); } }
存Session时用该类包装原始ResourceBundle即可:
ResourceBundle originalBundle = ResourceBundle.getBundle(basename,gotLocale); session.setAttribute("Bundle", new SerializableResourceBundle(originalBundle));
临时解决方案:屏蔽警告(不推荐)
如果确认你的应用永远不会用到会话持久化、不会做集群部署,可以直接给对应代码加上注解抑制IDE警告:
@SuppressWarnings("NonSerializableObjectBoundToHttpSession") session.setAttribute("Bundle",bundle);
该方案仅屏蔽警告,未解决实际问题,生产环境不建议使用。
内容的提问来源于stack exchange,提问作者DozezQuest
相关产品推荐
相关产品推荐

