博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot--模板从JSP到Freemarker的若干问题
阅读量:6766 次
发布时间:2019-06-26

本文共 4894 字,大约阅读时间需要 16 分钟。

hot3.png

一、乱码问题

JSP中的乱码问题一般处在<@page>头部,而在FreeMarker中为避免乱码,需要统一下配置:

#application.properties配置文件中添加spring.freemarker.settings.defaultEncoding=UTF-8spring.freemarker.charset=UTF-8spring.freemarker.content-type=text/html;charset=UTF-8

二、request中取值问题

如果你是从JSP转到了FreeMarker,除了语法上需要重新学习之外,还有很多在JSP中用的比较顺手的属性可能在FreeMarker中需要重新的认知了。

原先的EL表达式中有一些属性在FreeMarker中就不能使用了,例如常用到的${pageContext.request.contextPath}取绝对路径前缀的,在FreeMarker中恐怕不能做到了。
那么像EL中的requestScope和sessionScope还可以用不?其实在FreeMarker中是可以用的,取值范围有JspTaglibs、Application、Session、Request、RequestParameters,例如${Request.name}
不过因为FreeMarker的机制问题,这些前缀并不能像EL中那样省略。
上述的代码逻辑在FreeMarkerView.buildTemplateModel()中:

protected SimpleHash buildTemplateModel(Map
model, HttpServletRequest request, HttpServletResponse response) { AllHttpScopesHashModel fmModel = new AllHttpScopesHashModel(getObjectWrapper(), getServletContext(), request); fmModel.put(FreemarkerServlet.KEY_JSP_TAGLIBS, this.taglibFactory); fmModel.put(FreemarkerServlet.KEY_APPLICATION, this.servletContextHashModel); fmModel.put(FreemarkerServlet.KEY_SESSION, buildSessionModel(request, response)); fmModel.put(FreemarkerServlet.KEY_REQUEST, new HttpRequestHashModel(request, response, getObjectWrapper())); fmModel.put(FreemarkerServlet.KEY_REQUEST_PARAMETERS, new HttpRequestParametersHashModel(request)); fmModel.putAll(model); return fmModel; }

另外,我发现spring boot的配置中有这么两行:

spring.freemarker.expose-request-attributes=truespring.freemarker.expose-session-attributes=true

意思是把request和session中的属性导出到Freemarker的dataModel当中,这又是什么意思呢?

怀着好奇心,我探索了一下spring boot的源码,因为spring boot有很多自动装载的配置,我们找到FreeMarkerAutoConfiguration类:

@Configuration@ConditionalOnClass({ freemarker.template.Configuration.class,		FreeMarkerConfigurationFactory.class })@AutoConfigureAfter(WebMvcAutoConfiguration.class)@EnableConfigurationProperties(FreeMarkerProperties.class)public class FreeMarkerAutoConfiguration{    ...}

这里引用了FreeMarkerProperties属性文件,FreeMarkerProperties中有ExposeRequestAttributes属性。我们将在AbstractTemplateViewResolverProperties.applyToViewResolver()方法中找到痕迹:

public void applyToViewResolver(Object viewResolver) {        ...		resolver.setExposeRequestAttributes(isExposeRequestAttributes());        ...	}

然后在AbstractTemplateView中会用到这两个属性进行逻辑处理:

protected final void renderMergedOutputModel(			Map
model, HttpServletRequest request, HttpServletResponse response) throws Exception { if (this.exposeRequestAttributes) { for (Enumeration
en = request.getAttributeNames(); en.hasMoreElements();) { String attribute = en.nextElement(); if (model.containsKey(attribute) && !this.allowRequestOverride) { throw new ServletException("Cannot expose request attribute '" + attribute + "' because of an existing model object of the same name"); } Object attributeValue = request.getAttribute(attribute); if (logger.isDebugEnabled()) { logger.debug("Exposing request attribute '" + attribute + "' with value [" + attributeValue + "] to model"); } model.put(attribute, attributeValue); } } if (this.exposeSessionAttributes) { HttpSession session = request.getSession(false); if (session != null) { for (Enumeration
en = session.getAttributeNames(); en.hasMoreElements();) { String attribute = en.nextElement(); if (model.containsKey(attribute) && !this.allowSessionOverride) { throw new ServletException("Cannot expose session attribute '" + attribute + "' because of an existing model object of the same name"); } Object attributeValue = session.getAttribute(attribute); if (logger.isDebugEnabled()) { logger.debug("Exposing session attribute '" + attribute + "' with value [" + attributeValue + "] to model"); } model.put(attribute, attributeValue); } } } if (this.exposeSpringMacroHelpers) { if (model.containsKey(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE)) { throw new ServletException( "Cannot expose bind macro helper '" + SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + "' because of an existing model object of the same name"); } // Expose RequestContext instance for Spring macros. model.put(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE, new RequestContext(request, response, getServletContext(), model)); } applyContentType(response); renderMergedTemplateModel(model, request, response); }

我们看到这里进行了判断,如果exposeRequestAttributes为真,会把request和session中的属性复制到model中,也就是FreeMarker的root中。 其实流程最后还是到FreemarkerView类,可以参考他的renderMergedTemplateModel()方法。

现在我们可以明确了exposeRequestAttributes属性的作用了,和上面的用Request间接取值不同,用了exposeRequestAttributes之后,request中的属性被复制到了FreeMarker的root中了。
注意,用了exposeRequestAttributes之后,有时FreeMarker的root中属性名称和request中的属性名称一致,这样会出现冲突,可以添加以下属性解决这个问题,允许request中的属性覆盖原有root中的属性:

spring.freemarker.allow-request-override=truespring.freemarker.allow-session-override=true

转载于:https://my.oschina.net/lizaizhong/blog/1586702

你可能感兴趣的文章