这个问题本身不叫问题,可以直接写完整路径就行了,但是本人比较懒,不想写这么长一串,好吧,跟踪了半天的源代码,最后发现,
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() }); }
到此结束.嘿嘿.我去试试效果.
呃,忘了说用法了,我前面一篇中写过设置默认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()); } } }
<constant name="struts.freemarker.manager.classname" value="QFreemarkerManager" />
按道理来说,这里的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来帮我注入,
代码就改动这个,然后是配置文件,将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);
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>
经过我的实验,这样是可行的.