关于strust2 使用freemarker 指定模板路径前缀的纠结
2013-01-15 16:17
281 查看
这个问题本身不叫问题,可以直接写完整路径就行了,但是本人比较懒,不想写这么长一串,好吧,跟踪了半天的源代码,最后发现,
org.apache.struts2.views.freemarker.FreemarkerManager这个类createTemplateLoader方法用来搞这事的,
可惜两种都不符合我的要求,第一种是用classpath的,没办法,我的模板路径不是classpath下面的,第二种是文件路径,可惜它用的不是俺需要的路径,它是文件系统的路径,
也就是c:\这样的,我目前是放在web-inf下面的一个文件夹里面的,如果这两种都不符合的话,最后会创建一个在根路径下的TemplateLoader,也就是模板放在web应用的根路径下就可以找到了.
话说,struts2没有我这种需求吗?也许是我没有弄清楚.需要有高人解惑.
中间有一段小插曲,就是设置启动参数的时候,我将多个参数值都设置在了一个<context-param>下面,导致我的spring不能正常初始化了,承认这点忽视了,以后记得有多个参数就设置多个<context-param>.
最后的折中解决办法是继承一个freemarkerresult类,然后修改一下里面的逻辑,把我的前缀加在location前面吧,暂时就这样了.
但是接下来又发现了一个小问题,就是我的模板里面有包含别的模板文件,这个路径貌似也要写完整的,这样可不行啊,我只好又接着修改FreemarkerManager这个类了,将上面那个方法进行重载,咱们来继承这个类,暂且取名QFreemarkerManager了
其实也加了一行代码,就是当templatePath不为空的时候,你又不想用其他两种方式的时候就用第三种方式,然后你还是需要配置在web.xml里面配置context-param,如果你不想配置,那么还得再改FreemarkerManager的init方法了,因为templatePath就是从context-param中获得的.好了,现在可以配置纠结的模板前缀了----- "/WEB-INF/ftl"
到此结束.嘿嘿.我去试试效果.
呃,忘了说用法了,我前面一篇中写过设置默认result-type的,这个往里面设置参数,参数名字就是freemarkerManager,当然值就是我们的类了,这个可以看源代码就知道了.
为什么会这么麻烦呢,spring mvc里面配置freemarker直接有一个属性可以设置模板前缀路径.
相关代码在org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer这个类里面,不多说了.
实验结果,真无耐,发现无法給我注入,不知道它从哪里实例化这个该死的freemarkerManager了,暂时只能再将我前面的那个继承自freemarkerresult再拿出来用了.
-------------------------------------
昨天由于下班了,就没搞了,今天接着搞,看一下代码,freemarkerManager是用的@inject注解来注入的,跟踪代码看了一下,不知道从注入的实例freemarkerManager,人也看老了,
百度了一把,这个注解如果不指定名称的话,值是默认的,我猜想可能这个类被写在配置文件中了,于是在struts-default中找到了这个类的定义
既然这样,我就把它重新申明一遍好了,类名改成我的,然后再试一把效果.
哎,好吧,还是失败了,又百度了,点击打开链接在这边文章中找到了一些关于struts2内部IOC的知识.
在struts2启动的时候会初始化一些类,这个类org.apache.struts2.config.BeanSelectionProvider主要就是用来选择相关的类的,里面有一个方法register
它会根据Key 来选择相应的foundname,这个值又是用来查找注册的bean,如果没有注册的话,就会调用ObjectFactory实例化一个,经过实践,终于可以用我自己的FreemarkerManager了.
key就是这个struts.freemarker.manager.classname字符串,value就是类名,
按道理来说,这里的value也可以用一个bean的名称来代替,跟ObjectFactory类似,我们使用spring 的时候既可以用"spring"这个名称,也可以用类的全名称.这种方法还没实验,可以
参数spring-struts-plugin里面来配置.
好了,这个问题目前就到这里了,继续攻克其他的问题了.
今天,又碰到关于这个类的一个问题了,我写了个freemarker的自定义标签,继承的这个类TemplateDirectiveModel,但是不晓得怎么在struts2里面用起来,我参考了springmvc里面关于freemarkerconfigurer里面的代码,它的这个类里面是可以定义自已的变量的,但是struts的官方文档上面没提供相应的说明,只好参照spring里面的作法了,
好了,这下只能再修改Freemarker的其他方法了,修改就是init方法
红色字体的是我添加的,反正要修改,就顺便把这个也改了,关于这个path的问题,在上面捣鼓了很久,这次我把它用spring来帮我注入,
蓝色的是从spring mvc的freemarkerconfigurer中复制过来的,当然你得加一个freemarkerVariables的属性了,map类型的,
代码就改动这个,然后是配置文件,将QFreemarkerManager交給spring去实例化,
顺便在这里提一下这里写全类名与名称的区别,struts来选择bean的时候,如果你写的全类名,则将会用struts2的容器去实例化这个类,我们也就没有办法去注入咱们的属性了,
这里会有ClassNotFoundException的异常,也就是说你写bean 的id的话,会报异常,然后就调用
spring 的配置文件
这里我把templatePath也給注入进来了,换句话就不用在web.xml里面配置了,
经过我的实验,这样是可行的.
org.apache.struts2.views.freemarker.FreemarkerManager这个类createTemplateLoader方法用来搞这事的,
{ TemplateLoader templatePathLoader = null; try { if(templatePath!=null){ if (templatePath.startsWith("class://")) { // substring(7) is intentional as we "reuse" the last slash templatePathLoader = new ClassTemplateLoader(getClass(), templatePath.substring(7)); } else if (templatePath.startsWith("file://")) { templatePathLoader = new FileTemplateLoader(new File(templatePath.substring(7))); } } } catch (IOException e) { LOG.error("Invalid template path specified: " + e.getMessage(), e); } // presume that most apps will require the class and webapp template loader // if people wish to return templatePathLoader != null ? new MultiTemplateLoader(new TemplateLoader[]{ templatePathLoader, new WebappTemplateLoader(servletContext), new StrutsClassTemplateLoader() }) : new MultiTemplateLoader(new TemplateLoader[]{ new WebappTemplateLoader(servletContext), new StrutsClassTemplateLoader() }); }
可惜两种都不符合我的要求,第一种是用classpath的,没办法,我的模板路径不是classpath下面的,第二种是文件路径,可惜它用的不是俺需要的路径,它是文件系统的路径,
也就是c:\这样的,我目前是放在web-inf下面的一个文件夹里面的,如果这两种都不符合的话,最后会创建一个在根路径下的TemplateLoader,也就是模板放在web应用的根路径下就可以找到了.
话说,struts2没有我这种需求吗?也许是我没有弄清楚.需要有高人解惑.
中间有一段小插曲,就是设置启动参数的时候,我将多个参数值都设置在了一个<context-param>下面,导致我的spring不能正常初始化了,承认这点忽视了,以后记得有多个参数就设置多个<context-param>.
最后的折中解决办法是继承一个freemarkerresult类,然后修改一下里面的逻辑,把我的前缀加在location前面吧,暂时就这样了.
但是接下来又发现了一个小问题,就是我的模板里面有包含别的模板文件,这个路径貌似也要写完整的,这样可不行啊,我只好又接着修改FreemarkerManager这个类了,将上面那个方法进行重载,咱们来继承这个类,暂且取名QFreemarkerManager了
@Override protected TemplateLoader createTemplateLoader( ServletContext servletContext, String templatePath) { TemplateLoader templatePathLoader = null; try { if (templatePath != null) { if (templatePath.startsWith("class://")) { // substring(7) is intentional as we "reuse" the last slash templatePathLoader = new ClassTemplateLoader(getClass(), templatePath.substring(7)); } else if (templatePath.startsWith("file://")) { templatePathLoader = new FileTemplateLoader(new File( templatePath.substring(7))); } else { return new MultiTemplateLoader(new TemplateLoader[] { new WebappTemplateLoader(servletContext, templatePath), new StrutsClassTemplateLoader() }); } } } catch (IOException e) { LOG.error("Invalid template path specified: " + e.getMessage(), e); } // presume that most apps will require the class and webapp template // loader // if people wish to return templatePathLoader != null ? new MultiTemplateLoader( new TemplateLoader[] { templatePathLoader, new WebappTemplateLoader(servletContext), new StrutsClassTemplateLoader() }) : new MultiTemplateLoader(new TemplateLoader[] { new WebappTemplateLoader(servletContext), new StrutsClassTemplateLoader() }); }
其实也加了一行代码,就是当templatePath不为空的时候,你又不想用其他两种方式的时候就用第三种方式,然后你还是需要配置在web.xml里面配置context-param,如果你不想配置,那么还得再改FreemarkerManager的init方法了,因为templatePath就是从context-param中获得的.好了,现在可以配置纠结的模板前缀了----- "/WEB-INF/ftl"
到此结束.嘿嘿.我去试试效果.
呃,忘了说用法了,我前面一篇中写过设置默认result-type的,这个往里面设置参数,参数名字就是freemarkerManager,当然值就是我们的类了,这个可以看源代码就知道了.
为什么会这么麻烦呢,spring mvc里面配置freemarker直接有一个属性可以设置模板前缀路径.
相关代码在org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer这个类里面,不多说了.
实验结果,真无耐,发现无法給我注入,不知道它从哪里实例化这个该死的freemarkerManager了,暂时只能再将我前面的那个继承自freemarkerresult再拿出来用了.
-------------------------------------
昨天由于下班了,就没搞了,今天接着搞,看一下代码,freemarkerManager是用的@inject注解来注入的,跟踪代码看了一下,不知道从注入的实例freemarkerManager,人也看老了,
百度了一把,这个注解如果不指定名称的话,值是默认的,我猜想可能这个类被写在配置文件中了,于是在struts-default中找到了这个类的定义
<bean class="org.apache.struts2.views.freemarker.FreemarkerManager" name="struts" />
既然这样,我就把它重新申明一遍好了,类名改成我的,然后再试一把效果.
哎,好吧,还是失败了,又百度了,点击打开链接在这边文章中找到了一些关于struts2内部IOC的知识.
在struts2启动的时候会初始化一些类,这个类org.apache.struts2.config.BeanSelectionProvider主要就是用来选择相关的类的,里面有一个方法register
public void register(ContainerBuilder builder, LocatableProperties props) { alias(ObjectFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY, builder, props); alias(FileManagerFactory.class, StrutsConstants.STRUTS_FILE_MANAGER_FACTORY, builder, props, Scope.SINGLETON); alias(XWorkConverter.class, StrutsConstants.STRUTS_XWORKCONVERTER, builder, props); alias(TextProvider.class, StrutsConstants.STRUTS_XWORKTEXTPROVIDER, builder, props, Scope.DEFAULT); alias(LocaleProvider.class, StrutsConstants.STRUTS_LOCALE_PROVIDER, builder, props); alias(ActionProxyFactory.class, StrutsConstants.STRUTS_ACTIONPROXYFACTORY, builder, props); alias(ObjectTypeDeterminer.class, StrutsConstants.STRUTS_OBJECTTYPEDETERMINER, builder, props); alias(ActionMapper.class, StrutsConstants.STRUTS_MAPPER_CLASS, builder, props); alias(MultiPartRequest.class, StrutsConstants.STRUTS_MULTIPART_PARSER, builder, props, Scope.DEFAULT); alias(FreemarkerManager.class, StrutsConstants.STRUTS_FREEMARKER_MANAGER_CLASSNAME, builder, props);这个方法就是用来注册类的,可以看到调用了alias方法
void alias(Class type, String key, ContainerBuilder builder, Properties props, Scope scope) { if (!builder.contains(type)) { String foundName = props.getProperty(key, DEFAULT_BEAN_NAME); if (builder.contains(type, foundName)) { if (LOG.isInfoEnabled()) { LOG.info("Choosing bean (#0) for (#1)", foundName, type.getName()); } builder.alias(type, foundName, Container.DEFAULT_NAME); } else { try { Class cls = ClassLoaderUtil.loadClass(foundName, this.getClass()); if (LOG.isDebugEnabled()) { LOG.debug("Choosing bean (#0) for (#1)", cls.getName(), type.getName()); } builder.factory(type, cls, scope); } catch (ClassNotFoundException ex) { // Perhaps a spring bean id, so we'll delegate to the object factory at runtime if (LOG.isDebugEnabled()) { LOG.debug("Choosing bean (#0) for (#1) to be loaded from the ObjectFactory", foundName, type.getName()); } if (DEFAULT_BEAN_NAME.equals(foundName)) { // Probably an optional bean, will ignore } else { if (ObjectFactory.class != type) { builder.factory(type, new ObjectFactoryDelegateFactory(foundName, type), scope); } else { throw new ConfigurationException("Cannot locate the chosen ObjectFactory implementation: " + foundName); } } } } } else { if (LOG.isWarnEnabled()) { LOG.warn("Unable to alias bean type (#0), default mapping already assigned.", type.getName()); } } }
它会根据Key 来选择相应的foundname,这个值又是用来查找注册的bean,如果没有注册的话,就会调用ObjectFactory实例化一个,经过实践,终于可以用我自己的FreemarkerManager了.
<constant name="struts.freemarker.manager.classname" value="QFreemarkerManager" />
key就是这个struts.freemarker.manager.classname字符串,value就是类名,
按道理来说,这里的value也可以用一个bean的名称来代替,跟ObjectFactory类似,我们使用spring 的时候既可以用"spring"这个名称,也可以用类的全名称.这种方法还没实验,可以
参数spring-struts-plugin里面来配置.
好了,这个问题目前就到这里了,继续攻克其他的问题了.
今天,又碰到关于这个类的一个问题了,我写了个freemarker的自定义标签,继承的这个类TemplateDirectiveModel,但是不晓得怎么在struts2里面用起来,我参考了springmvc里面关于freemarkerconfigurer里面的代码,它的这个类里面是可以定义自已的变量的,但是struts的官方文档上面没提供相应的说明,只好参照spring里面的作法了,
好了,这下只能再修改Freemarker的其他方法了,修改就是init方法
@Override public void init(ServletContext servletContext) throws TemplateException { config = createConfiguration(servletContext); // Set defaults: config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER); contentType = DEFAULT_CONTENT_TYPE; // Process object_wrapper init-param out of order: wrapper = createObjectWrapper(servletContext); if (LOG.isDebugEnabled()) { LOG.debug("Using object wrapper of class " + wrapper.getClass().getName()); } config.setObjectWrapper(wrapper); // Process TemplatePath init-param out of order: if (templatePath==null) { templatePath = servletContext.getInitParameter(INITPARAM_TEMPLATE_PATH); } if (templatePath == null) { templatePath = servletContext.getInitParameter("templatePath"); } config.setTemplateLoader(createTemplateLoader(servletContext, templatePath)); if (!CollectionUtils.isEmpty(this.freemarkerVariables)) { config.setAllSharedVariables(new SimpleHash( this.freemarkerVariables, config.getObjectWrapper())); } loadSettings(servletContext); }
红色字体的是我添加的,反正要修改,就顺便把这个也改了,关于这个path的问题,在上面捣鼓了很久,这次我把它用spring来帮我注入,
蓝色的是从spring mvc的freemarkerconfigurer中复制过来的,当然你得加一个freemarkerVariables的属性了,map类型的,
代码就改动这个,然后是配置文件,将QFreemarkerManager交給spring去实例化,
<constant name="struts.freemarker.manager.classname" value="freemarkerManager" />将struts.xml中这句话改一下,value这个时候是spring 的id名称,就不是类的全名了,
顺便在这里提一下这里写全类名与名称的区别,struts来选择bean的时候,如果你写的全类名,则将会用struts2的容器去实例化这个类,我们也就没有办法去注入咱们的属性了,
Class cls = ClassLoaderUtil.loadClass(foundName, this.getClass()); if (LOG.isDebugEnabled()) { LOG.debug("Choosing bean (#0) for (#1)", cls.getName(), type.getName()); } builder.factory(type, cls, scope);
这里会有ClassNotFoundException的异常,也就是说你写bean 的id的话,会报异常,然后就调用
if (ObjectFactory.class != type) { builder.factory(type, new ObjectFactoryDelegateFactory(foundName, type), scope); } else { throw new ConfigurationException("Cannot locate the chosen ObjectFactory implementation: " + foundName); }bean 工厂来实例化,这个时候就是咱们spring出马的时候了,这时就可以在spring配置属性了,
spring 的配置文件
<bean id="freemarkerManager" class="com.qcms.cms.result.QFreemarkerManager"> <property name="templatePath" value="/WEB-INF"/> <property name="freemarkerVariables"> <map> <entry key="h" value-ref="h"/> </map> </property> </bean>
这里我把templatePath也給注入进来了,换句话就不用在web.xml里面配置了,
经过我的实验,这样是可行的.
相关文章推荐
- 关于IIS报【指定的路径无法在此使用】的错误
- java实现word套打(关于Freemarker生成word的使用(java生成word))
- 【Lua】关于遍历指定路径下所有目录及文件
- Spring Boot使用模板freemarker【从零开始学Spring Boot(转)
- spring boot 使用FreeMarker模板
- 关于STL模板的使用效率与控制输出时最后不加空格问题
- 关于CKEditor4.5.6的使用,自定义toolbar配置,上传图片案例(SpringMVC+MyBatis案例),自定义行高,去编辑器的中内容,将编辑器中内容设置到指定的位置等
- Spring Boot使用模板freemarker的示例代码
- 关于使用Intellij Idea时java系统找不到指定文件的解决方案
- django模板中关于过滤器的使用
- Spring 4 使用Freemarker模板发送邮件&添加附件
- 使用C++0x新特性为模板参数指定约束条件
- FreeMarker模板使用方法讲解
- 关于PHP模板Smarty的初级使用方法以及心得分享
- 关于Windows无法访问指定设备路径或文件,您可能没有合适的权限访问问题解决<转>
- 使用freemarker生成xml模板
- ModelAttribute的使用 关于请求参数和URL模板
- 关于JDK与Tomcat安装路径修改过后无法正常使用的解放方案
- 关于WEB-INF下的jsp,使用绝对路径如何访问?