目標
使用tile時,我不喜歡tiles.xml
的默認(?)方法。 我不想將JS和CSS的導入,頁面標題,導航,正文等放入各自的文件中,如下面的代碼片段所示,因為這使我可以在編輯器窗口之間切換。
<definition name='hello' template='/WEB-INF/templates/main.jsp'><put-attribute name='title' value='Hello' type='string' /><put-attribute name='head' value='/WEB-INF/templates/hello-js-and-css.jsp' /><put-attribute name='nav' value='/WEB-INF/templates/hello-nav.jsp' /><put-attribute name='body' value='/WEB-INF/templates/hello.jsp' />
</definition>
顯然,我也不想在tiles.xml
放置太多細節。
我真正喜歡的是每頁一個文件,將模板組裝在一個位置,例如這段JSP:
<tiles:insertTemplate template='template.jsp'><tiles:putAttribute name='title' value='Hello' /><tiles:putAttribute name='head'><script type='text/javascript' src='/js/jQuery.js' /><script type='text/javascript' src='/js/hello.js' /></tiles:putAttribute><tiles:putAttribute name='body'><div>Hello, world!</div></tiles:putAttribute>
</tiles:insertTemplate>
在Velocity中,應該看起來像這樣 :
#tiles_insertTemplate({'template': 'template.vm'})#tiles_putAttribute({'name':'title', 'value': 'Hello'})#end#tiles_putAttribute({'name':'head'})<script type='text/javascript' src='/js/jQuery.js' /><script type='text/javascript' src='/js/hello.js' />#end#tiles_putAttribute({'name':'body'})<div>Hello, world!</div>#end
#end
但是, 有關集成的文檔確實旨在向基于Tiles的應用程序添加一些Velocity支持,而我想要的卻恰恰相反:在我豐富的Velocity應用程序中使用Tiles,并完全支持spring上下文,宏等。
解
簡而言之,我們要做的是:
- 使用
VelocityViewResolver
解析和渲染頁面 - 向此Velocity渲染引擎添加對Tiles宏的支持
- 擴展Tiles渲染器以完全支持Velocity,包括Spring上下文,宏等。最終,我們將使用Spring創建的原始Velocity引擎。
GitHub上以最小,完整的Web應用程序形式提供了完整的源代碼。 有關詳細信息,請參見下文。
彈簧和速度->瓷磚
第一步,我們像這樣定義viewResolver
和velocityConfig
:
@Bean
public VelocityConfig velocityConfig() {VelocityConfigurer cfg = new VelocityConfigurer();cfg.setResourceLoaderPath('/WEB-INF/velocity/');cfg.setConfigLocation(context.getResource('/WEB-INF/velocity.properties'));return cfg;
}@Bean
public ViewResolver viewResolver() {VelocityViewResolver resolver = new VelocityViewResolver();resolver.setViewClass(VelocityToolboxView.class);resolver.setSuffix('.vm');return resolver;
}
重要的是我們在那里使用VelocityToolboxView
,否則tile指令將不起作用。
我們還需要在velocity.properties
以下內容:
userdirective=org.apache.tiles.velocity.template.AddAttributeDirective,\org.apache.tiles.velocity.template.AddListAttributeDirective,\org.apache.tiles.velocity.template.DefinitionDirective,\org.apache.tiles.velocity.template.GetAsStringDirective,\org.apache.tiles.velocity.template.ImportAttributeDirective,\org.apache.tiles.velocity.template.InsertAttributeDirective,\org.apache.tiles.velocity.template.InsertDefinitionDirective,\org.apache.tiles.velocity.template.InsertTemplateDirective,\org.apache.tiles.velocity.template.PutAttributeDirective,\org.apache.tiles.velocity.template.PutListAttributeDirective
這為Velocity增加了對Tiles指令的基本支持,但是它仍然沒有用,因為一旦Velocity將渲染移交給Tiles,Tile就無法渲染Velocity并只會忽略它(將#directives
語法渲染到瀏覽器。
瓷磚->速度
我們需要教導Tiles使用Velocity。 為此,我們將需要一個自定義的TilesInitializer
:
@Bean
public TilesConfigurer tilesConfigurer() {TilesConfigurer cfg = new TilesConfigurer();cfg.setTilesInitializer(new VelocityTilesInitializer(velocityConfig()));return cfg;
}
public class VelocityTilesInitializer extends DefaultTilesInitializer {private VelocityConfig velocityConfig;public VelocityTilesInitializer(VelocityConfig velocityConfig) {this.velocityConfig = velocityConfig;}@Overrideprotected AbstractTilesContainerFactory createContainerFactory(TilesApplicationContext context) {return new BasicTilesContainerFactory() {@Overrideprotected List<TilesRequestContextFactory> getTilesRequestContextFactoriesToBeChained(ChainedTilesRequestContextFactory parent) {List<TilesRequestContextFactory> factories = super.getTilesRequestContextFactoriesToBeChained(parent);registerRequestContextFactory(VelocityTilesRequestContextFactory.class.getName(),factories, parent);return factories;}@Overrideprotected AttributeRenderer createTemplateAttributeRenderer(BasicRendererFactory rendererFactory,TilesApplicationContext applicationContext,TilesRequestContextFactory contextFactory,TilesContainer container,AttributeEvaluatorFactory attributeEvaluatorFactory) {ContextPassingVelocityAttributeRenderer var = new ContextPassingVelocityAttributeRenderer(velocityConfig.getVelocityEngine());var.setApplicationContext(applicationContext);var.setRequestContextFactory(contextFactory);var.setAttributeEvaluatorFactory(attributeEvaluatorFactory);var.commit();return var;}};}
}
我們快到了,但是有些棘手。 通常在第31-32行中,您將放置velocityAttributeRenderer
。 但是,此渲染器完全忽略了Tiles從Velocity接收到的Spring增強的Velocity上下文和引擎。 它創建自己的VelocityEngine
并進行渲染,丟棄所有Spring和tile指令以及上下文對象。
在Tiles中無法更改此行為(否則在設計模式和可擴展性方面似乎是一項有趣的研究)。 我什至為它創建了兩個JIRA問題: 541用于轉發上下文 , 542用于注入VelocityEngine
。
同時,我們必須解決此變通方法(有關完整源,請參見github ):
public class ContextPassingVelocityAttributeRenderer extendsAbstractTypeDetectingAttributeRenderer {// ...private VelocityEngine engine;public ContextPassingVelocityAttributeRenderer(VelocityEngine engine) {this.engine = engine;}// ...public void commit() {velocityView = new VelocityView(new TilesApplicationContextJeeConfig());velocityView.setVelocityEngine(engine);}@Overridepublic void write(Object value, Attribute attribute,TilesRequestContext request) throws IOException {if (value != null) {if (value instanceof String) {InternalContextAdapter adapter = (InternalContextAdapter) ((VelocityTilesRequestContext) request).getRequestObjects()[0];Context context = adapter.getInternalUserContext();Template template = velocityView.getTemplate((String) value);velocityView.merge(template, context, request.getWriter());} else {throw new InvalidTemplateException('Cannot render a template that is not a string: '+ value.toString());}} else {throw new InvalidTemplateException('Cannot render a null template');}}// ...
它可以解決JIRA的兩個問題,并讓我們實現最終目標:
- 該
VelocityEngine
注入VelocityView
原來這里是VelocityEngine
從春天。 除其他外,它支持Spring指令和上下文相關工具。 -
write
方法中的TilesRequestContext
仍然包含從Spring腳手架創建的原始Velocity上下文。VelocityAttributeRenderer
標準實現只是將其丟棄。 上面的解決方法提取原始上下文并將其用于呈現。
結論
這段旅程花了比我想象更多的時間。 尚無此類案例的文檔,因此我花了數小時進行調試,閱讀源代碼,進行實驗,祈禱和詛咒。 當我對Spring視圖分辨率和渲染引擎以及Tiles和Velocity的內部知識幾乎為零時,它變得更加有趣。
自從我學到了很多有關所有這些技術的知識之后,最終能夠以一種相當優雅的方式解決它,這是非常令人滿意的。 但這也是一個令人沮喪且耗時的難題,我希望這篇文章可以避免給別人帶來麻煩。
更新–速度工具
不久之后,我發現此解決方案不支持Velocity Tools屬性。 添加方法如下: Spring&Velocity Tools 。
參考:來自我們的JCG合作伙伴 Konrad Garus的Spring,Velocity和Tiles的集成,在Squirrel的博客上。
翻譯自: https://www.javacodegeeks.com/2012/07/integrating-spring-velocity-and-tiles.html