整合彈簧,速度和瓷磚
目標
當我使用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;
}
@Override
protected AbstractTilesContainerFactory createContainerFactory(
TilesApplicationContext context) {
return new BasicTilesContainerFactory() {
@Override
protected List<TilesRequestContextFactory> getTilesRequestContextFactoriesToBeChained(
ChainedTilesRequestContextFactory parent) {
List<TilesRequestContextFactory> factories = super
.getTilesRequestContextFactoriesToBeChained(parent);
registerRequestContextFactory(
VelocityTilesRequestContextFactory.class.getName(),
factories, parent);
return factories;
}
@Override
protected 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 extends
AbstractTypeDetectingAttributeRenderer {
// ...
private VelocityEngine engine;
public ContextPassingVelocityAttributeRenderer(VelocityEngine engine) {
this.engine = engine;
}
// ...
public void commit() {
velocityView = new VelocityView(new TilesApplicationContextJeeConfig());
velocityView.setVelocityEngine(engine);
}
@Override
public 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