1. 程式人生 > 其它 >整合彈簧,速度和瓷磚

整合彈簧,速度和瓷磚

技術標籤:設計模式javahtmljsspring

我喜歡Tiles,並且聽到了很多有關Velocity的資訊。 它們似乎有不同的用途,並且據說很容易結合在一起,所以我決定試一試,並在Spring Web應用程式中同時使用它們。 整合實際上花費了許多小時,並且是一次真正的過山車,在此期間,我對這三種技術都學到了很多東西。 希望這篇文章可以使別人免於這種樂趣,並使他們專注於業務。

目標

當我使用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上下文,巨集等。

簡而言之,我們要做的是:

  1. 使用VelocityViewResolver解析和渲染頁面
  2. 向此Velocity渲染引擎新增對Tiles巨集的支援
  3. 擴充套件Tiles渲染器以全面支援Velocity,包括Spring上下文,巨集等。最終,我們將使其使用Spring建立的原始Velocity引擎。

GitHub上以最小,完整的Web應用程式形式提供了完整的原始碼。 有關詳細資訊,請參見下文。

彈簧和速度->瓷磚

第一步,我們像這樣定義viewResolvervelocityConfig

@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的兩個問題,並讓我們實現最終目標:

  1. VelocityEngine注入VelocityView原來這裡是VelocityEngine從春天。 除其他外,它支援Spring指令和上下文相關工具。
  2. 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