Freemarker URLTemplate 加载器将国际化添加到模板名称和多个加载器不工作
Freemarker URLTemplate loader adding internationalization to the template name and multiple loaders not working
我有一个 Spring MVC 应用程序,我正在尝试将 freemarker 作为视图解析器。我正在尝试设置它,以便它先检查不同的服务器,然后在本地检查。这是相关代码:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/ftl/"/>
<property name="preTemplateLoaders" ref="myTemplateLoader"/>
</bean>
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
<property name="order" value="2" />
</bean>
<bean id="myTemplateLoader" class="com.xxx.MyURLTemplateLoader">
<property name="baseUrl" value="http://10.0.0.5:8080/ftl/"/>
</bean>
class 看起来像这样:
public class MyURLTemplateLoader extends URLTemplateLoader {
private String baseUrl;
@Override
protected URL getURL(String arg0){
URL url = null;
try {
url = new URL(baseUrl+arg0);
System.out.println("URL Being used:"+url.toString());
} catch (Exception s) {
s.printStackTrace();
}
return url;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
}
控制器就是这么简单:
ModelAndView view = new ModelAndView("index2");
return view;
现在,当我 运行 这时,我收到 404 错误。奇怪的是我在日志中得到了这个:
URL Being used:http://10.0.0.5:8080/ftl/index2_en_US.ftl
那为什么要将“_en_US”添加到我的 URL 中?
此外,如果我删除 preTemplateLoaders,它工作正常,它可以很好地获取本地模板。但是添加了URLTemplateLoader,不仅模板名称添加了_en_US,而且在本地也找不到。
FreeMarker 有一些不幸的遗留配置默认值,其中之一是 localized_lookup
默认为 true
。这就是为什么它首先尝试找到 index2_en_US.ftl
,然后如果找不到 index2_en.ftl
,最后 index2.ftl
。另外,显然您正在使用的 TemplateLoader
实现有一个不正确的 findTemplateSource
方法,因此上述回退机制不起作用。所以,findTemplateSource
应该是固定的,而且你想关闭本地化查找:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
...
<property name="freemarkerSettings">
<props>
<prop key="localized_lookup">false</prop>
<!-- Some unrelated but useful default overrides: -->
<prop key="incompatible_improvements">2.3.23</prop>
<prop key="template_exception_handler">rethrow</prop>
<prop key="log_template_exceptions">false</prop>
<prop key="default_encoding">UTF-8</prop>
</props>
</property>
</bean>
感谢 ddekany 解决本地化问题。就这么修好了
然而,第二个问题要难得多。
事实证明有一个 "undocumented feature" 如果您设置模板加载器它会清除默认加载器 (org.springframework.ui.freemarker.SpringTemplateLoader) 所以如果您想要 2 个(或更多)加载器,您需要自己动手。不幸的是,SpringTemplateLoader 将 ServletContext 作为构造参数,您不能(轻松地)在 context.xml 文件中定义它。
所以我所做的是 re-write SpringTemplateLoader 一点点让它知道 ServletContext。这是我的代码:
public class MyWebappTemplateLoader implements TemplateLoader, ServletContextAware {
static final Logger logger = LoggerFactory.getLogger(MyWebappTemplateLoader.class);
private ResourceLoader resourceLoader = null;
private String templateLoaderPath = null;
private ServletContext servletContext;
private void init() {
GenericWebApplicationContext context = new GenericWebApplicationContext(servletContext);
resourceLoader = (ResourceLoader)context;
if (logger.isInfoEnabled()) {
logger.info("SpringTemplateLoader for FreeMarker: using resource loader [" + this.resourceLoader +
"] and template loader path [" + this.templateLoaderPath + "]");
}
}
public Object findTemplateSource(String name) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for FreeMarker template with name [" + name + "]");
}
if(this.resourceLoader==null) {
init();
}
Resource resource = this.resourceLoader.getResource(this.templateLoaderPath + name);
return (resource.exists() ? resource : null);
}
public Reader getReader(Object templateSource, String encoding) throws IOException {
Resource resource = (Resource) templateSource;
try {
return new InputStreamReader(resource.getInputStream(), encoding);
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find FreeMarker template: " + resource);
}
throw ex;
}
}
public long getLastModified(Object templateSource) {
return -1;
}
public void closeTemplateSource(Object templateSource) throws IOException {
}
public void setTemplateLoaderPath(String arg0) {
if (!arg0.endsWith("/")) {
arg0 += "/";
}
this.templateLoaderPath = arg0;
}
@Override
public void setServletContext(ServletContext arg0) {
this.servletContext = arg0;
}
}
我的 xml 现在看起来像这样:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="preTemplateLoaders" ref="multiTemplateLoader"/>
<property name="freemarkerSettings">
<props>
<prop key="default_encoding">UTF-8</prop>
<prop key="localized_lookup">false</prop>
</props>
</property>
</bean>
<bean id="fremarkerViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
<property name="order" value="2" />
</bean>
<bean id="myUrlTemplateLoader" class="com.xxx.MyURLTemplateLoader">
<property name="baseUrl" value="http://10.0.0.5:8080/ftl/"/>
</bean>
<bean id="defaultTemplateLoader" class="com.xxx.MyWebappTemplateLoader">
<property name="templateLoaderPath" value="/WEB-INF/ftl/" />
</bean>
<bean id="multiTemplateLoader" class="freemarker.cache.MultiTemplateLoader">
<constructor-arg>
<list>
<ref bean="myUrlTemplateLoader"/>
<ref bean="defaultTemplateLoader"/>
</list>
</constructor-arg>
</bean>
还有最后一件事。如果您希望加载程序转到下一个加载程序,则 URLTemplateLoader 必须 return 为 NULL。所以我必须将它添加到我的 URLTemplateLoader:
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
if(conn.getResponseCode()!=200) {
return null;
}
如果有人知道更好的方法,我很乐意看到它!
我有一个 Spring MVC 应用程序,我正在尝试将 freemarker 作为视图解析器。我正在尝试设置它,以便它先检查不同的服务器,然后在本地检查。这是相关代码:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/ftl/"/>
<property name="preTemplateLoaders" ref="myTemplateLoader"/>
</bean>
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
<property name="order" value="2" />
</bean>
<bean id="myTemplateLoader" class="com.xxx.MyURLTemplateLoader">
<property name="baseUrl" value="http://10.0.0.5:8080/ftl/"/>
</bean>
class 看起来像这样:
public class MyURLTemplateLoader extends URLTemplateLoader {
private String baseUrl;
@Override
protected URL getURL(String arg0){
URL url = null;
try {
url = new URL(baseUrl+arg0);
System.out.println("URL Being used:"+url.toString());
} catch (Exception s) {
s.printStackTrace();
}
return url;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
}
控制器就是这么简单:
ModelAndView view = new ModelAndView("index2");
return view;
现在,当我 运行 这时,我收到 404 错误。奇怪的是我在日志中得到了这个:
URL Being used:http://10.0.0.5:8080/ftl/index2_en_US.ftl
那为什么要将“_en_US”添加到我的 URL 中?
此外,如果我删除 preTemplateLoaders,它工作正常,它可以很好地获取本地模板。但是添加了URLTemplateLoader,不仅模板名称添加了_en_US,而且在本地也找不到。
FreeMarker 有一些不幸的遗留配置默认值,其中之一是 localized_lookup
默认为 true
。这就是为什么它首先尝试找到 index2_en_US.ftl
,然后如果找不到 index2_en.ftl
,最后 index2.ftl
。另外,显然您正在使用的 TemplateLoader
实现有一个不正确的 findTemplateSource
方法,因此上述回退机制不起作用。所以,findTemplateSource
应该是固定的,而且你想关闭本地化查找:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
...
<property name="freemarkerSettings">
<props>
<prop key="localized_lookup">false</prop>
<!-- Some unrelated but useful default overrides: -->
<prop key="incompatible_improvements">2.3.23</prop>
<prop key="template_exception_handler">rethrow</prop>
<prop key="log_template_exceptions">false</prop>
<prop key="default_encoding">UTF-8</prop>
</props>
</property>
</bean>
感谢 ddekany 解决本地化问题。就这么修好了
然而,第二个问题要难得多。
事实证明有一个 "undocumented feature" 如果您设置模板加载器它会清除默认加载器 (org.springframework.ui.freemarker.SpringTemplateLoader) 所以如果您想要 2 个(或更多)加载器,您需要自己动手。不幸的是,SpringTemplateLoader 将 ServletContext 作为构造参数,您不能(轻松地)在 context.xml 文件中定义它。
所以我所做的是 re-write SpringTemplateLoader 一点点让它知道 ServletContext。这是我的代码:
public class MyWebappTemplateLoader implements TemplateLoader, ServletContextAware {
static final Logger logger = LoggerFactory.getLogger(MyWebappTemplateLoader.class);
private ResourceLoader resourceLoader = null;
private String templateLoaderPath = null;
private ServletContext servletContext;
private void init() {
GenericWebApplicationContext context = new GenericWebApplicationContext(servletContext);
resourceLoader = (ResourceLoader)context;
if (logger.isInfoEnabled()) {
logger.info("SpringTemplateLoader for FreeMarker: using resource loader [" + this.resourceLoader +
"] and template loader path [" + this.templateLoaderPath + "]");
}
}
public Object findTemplateSource(String name) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for FreeMarker template with name [" + name + "]");
}
if(this.resourceLoader==null) {
init();
}
Resource resource = this.resourceLoader.getResource(this.templateLoaderPath + name);
return (resource.exists() ? resource : null);
}
public Reader getReader(Object templateSource, String encoding) throws IOException {
Resource resource = (Resource) templateSource;
try {
return new InputStreamReader(resource.getInputStream(), encoding);
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find FreeMarker template: " + resource);
}
throw ex;
}
}
public long getLastModified(Object templateSource) {
return -1;
}
public void closeTemplateSource(Object templateSource) throws IOException {
}
public void setTemplateLoaderPath(String arg0) {
if (!arg0.endsWith("/")) {
arg0 += "/";
}
this.templateLoaderPath = arg0;
}
@Override
public void setServletContext(ServletContext arg0) {
this.servletContext = arg0;
}
}
我的 xml 现在看起来像这样:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="preTemplateLoaders" ref="multiTemplateLoader"/>
<property name="freemarkerSettings">
<props>
<prop key="default_encoding">UTF-8</prop>
<prop key="localized_lookup">false</prop>
</props>
</property>
</bean>
<bean id="fremarkerViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
<property name="order" value="2" />
</bean>
<bean id="myUrlTemplateLoader" class="com.xxx.MyURLTemplateLoader">
<property name="baseUrl" value="http://10.0.0.5:8080/ftl/"/>
</bean>
<bean id="defaultTemplateLoader" class="com.xxx.MyWebappTemplateLoader">
<property name="templateLoaderPath" value="/WEB-INF/ftl/" />
</bean>
<bean id="multiTemplateLoader" class="freemarker.cache.MultiTemplateLoader">
<constructor-arg>
<list>
<ref bean="myUrlTemplateLoader"/>
<ref bean="defaultTemplateLoader"/>
</list>
</constructor-arg>
</bean>
还有最后一件事。如果您希望加载程序转到下一个加载程序,则 URLTemplateLoader 必须 return 为 NULL。所以我必须将它添加到我的 URLTemplateLoader:
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
if(conn.getResponseCode()!=200) {
return null;
}
如果有人知道更好的方法,我很乐意看到它!