// 生成 Res Res res = I18n.use(this.getBaseName(), localLanguage); try { // ThreadLocal 方案,给 Service 用 I18nContext.set(res); // 视图层需要 _res 的话,保持你现在的时机 if (!this.isSwitchView){ c.setAttr(this.getResName(), res); } // invoke 前后都打印一下 Res before = (Res) c.getAttr("_res"); System.out.println("[i18n] BEFORE invoke _res=" + (before==null?null:before.get("button.importing"))); inv.invoke(); Res after = (Res) c.getAttr("_res"); System.out.println("[i18n] AFTER invoke _res=" + (after==null?null:after.get("button.importing"))); System.out.println("[i18n] SAME OBJ? " + (before == after)); if (this.isSwitchView) { this.switchView(localLanguage, c); } } finally { // 避免线程复用导致串语言 I18nContext.clear(); }
我有一个国际化代码是这样的,我想在control层也取到国际化语言,然后我就在invoke之前
c.setAttr(this.getResName(), res);
但是有个问题前端渲染界面的
TemplateRender
public void render() { this.response.setContentType(this.getContentType()); Map<Object, Object> data = new HashMap(); Enumeration<String> attrs = this.request.getAttributeNames(); while(attrs.hasMoreElements()) { String attrName = (String)attrs.nextElement(); data.put(attrName, this.request.getAttribute(attrName)); } try { OutputStream os = this.response.getOutputStream(); engine.getTemplate(this.view).render(data, os); } catch (Exception var4) { throw new RenderException(var4); } }
这段render方法里面吧Request里面的请求参数都放到data里面,这里也有一个国际化参数_res,他取到的值是旧的(用户第一次登录选择英语,第二次登录选择中文,这个_res的值就是英语),这是什么原因呢?
解决是解决了,把serAttr放到invoke之后就正常了,我想知道,是谁改了这个参数,不是invoke之前设置了,之后肯定也能用吗??
@Before(CustomerCacheInterceptor.class)
@CacheName("getMenu")
public void menu() {
}
protected void useCacheDataAndRender(Map cacheData, Controller controller) {
HttpServletRequest request = controller.getRequest();
Set> set = cacheData.entrySet();
Iterator> it = set.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
request.setAttribute((String)entry.getKey(), entry.getValue());
}
request.removeAttribute("_renderKey");
RenderInfo renderInfo = (RenderInfo)cacheData.get("_renderKey");
if (renderInfo != null) {
controller.render(renderInfo.createRender());
}
}
CustomerCacheInterceptor程序将页面缓存后,如果命中缓存会从缓存中取得信息。并覆盖掉res的国际化信息。这就导致他缓存的界面是第一次请求界面的信息,用户选的什么语言就是什么语言。另外这个还是不分用户的,其他用户进来时候取出来的语言也会是这个第一次缓存的语言数据。
当把c.setAttr(this.getResName(), res);放在了invoke之后,就表示他会执行完其他所有拦截器之后,从新把attr设置进去。
然后在TemplateRender会从新从Request里面取得新的res。
PS:这是作者有意这么控制的还是无意的啊。 I18nInterceptor故意吧c.setAttr(this.getResName(), res);放在invoke之后,然后TemplateRender,又故意从this.request.getAttributeNames();取得设置的国际化参数,再使用render方法实现的国际化。
算是国际化里面一个巨难查的隐藏BUG?