2013-07-18 105 views
1

有段時間我正在使用Spring框架開發Web應用程序,並且一切正常。但最近事實證明,我將不得不向我的網頁介紹一些Ajax電話。經過一些Google搜索之後,我發現Tiles2在使用參數'fragments = nameOfTile'接收ajax調用時爲解析視圖提供了很好的支持。爲了實現你必須使用org.springframework.js.ajax.AjaxUrlBasedViewResolverwhich和org.springframework.js.ajax.tiles2.AjaxTilesView。NoClassDefFoundError在Spring MVC 3中使用AjaxUrlBasedViewResolver 3

下面是我的看法解析器配置(請注意,我用的TILES.3.0.1):

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer"> 
    <property name="definitions" value="WEB-INF/tiles-config.xml"/> 
</bean> 
<bean id="ajaxViewResolver" class="org.springframework.js.ajax.AjaxUrlBasedViewResolver" p:order="0"> 
     <property name="viewClass" value="org.springframework.js.ajax.tiles2.AjaxTilesView"/> 
</bean> 
<bean id="viewResolver" class="org.springframework.web.servlet.view.tiles3.TilesViewResolver" p:order="1"> 
</bean> 

至於我,當我使用這個wasnt前面所提到的:

<bean id="ajaxViewResolver" class="org.springframework.js.ajax.AjaxUrlBasedViewResolver" p:order="0"> 
     <property name="viewClass" value="org.springframework.js.ajax.tiles2.AjaxTilesView"/> 
</bean> 

一切都很好,但是當我將jaxViewResolver引入到我的代碼中時。我要求什麼都頁(通常和AJAX調用)我得到這樣的響應:

HTTP Status 500 - Handler processing failed; nested exception is java.lang.NoClassDefFoundError: org/apache/tiles/TilesApplicationContext 

所以我的問題是:

  • 我可以使用AjaxUrlBasedViewResolver與Tiles3一起?
  • 如果是,如何解決我的問題,可能是什麼原因?
  • 如果沒有,是否有任何「準備好」解決方案?

感謝您的幫助, ķ

編輯#1:@Bar說: 「你有沒有包括彈簧的Webflow罐springsource.org/spring-web-flow#download」

那麼我使用Maven作爲我的依賴管理器。下面你可以看到我的pom.xml:

<dependency> 
     <groupId>junit</groupId> 
     <artifactId>junit</artifactId> 
     <version>3.8.1</version> 
     <scope>test</scope> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-webmvc</artifactId> 
     <version>3.2.2.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>javax.servlet</groupId> 
     <artifactId>servlet-api</artifactId> 
     <version>2.5</version> 
     <scope>provided</scope> 
    </dependency> 
     <dependency> 
     <groupId>javax.servlet</groupId> 
     <artifactId>jstl</artifactId> 
     <version>1.2</version> 
     </dependency> 
    <dependency> 
     <groupId>org.apache.tiles</groupId> 
     <artifactId>tiles-jsp</artifactId> 
     <version>3.0.1</version> 
    </dependency> 
    <dependency> 
     <groupId>org.slf4j</groupId> 
     <artifactId>slf4j-log4j12</artifactId> 
     <version>1.5.6</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.webflow</groupId> 
     <artifactId>spring-webflow</artifactId> 
     <version>2.3.2.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>javax.validation</groupId> 
     <artifactId>validation-api</artifactId> 
    <version>1.0.0.GA</version> 
    </dependency> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-validator-annotation-processor</artifactId> 
     <version>4.1.0.Final</version> 
    </dependency> 

所以應該包括它。

+0

您是否包含了spring-webflow jar? http://www.springsource.org/spring-web-flow#download – Bart

+0

@Bart檢查我的編輯#1 – DonCziken

回答

5

好的,所以我檢測到了這個問題。從尾部3開始,RequestContext上面的抽象已經改變,org.springframework.js.ajax.tiles2.AjaxTilesView正在使用舊的。這就是爲什麼它不工作,它是參考不存在的類。

經過一些實驗和通過尾巴3 javadocs我已經設法重寫這個(AjaxTilesView)類,並採用它在尾巴3環境中工作。我做了很少的測試,現在它工作正常。唯一的問題是你必須在你的ajax請求或附加參數中指定header,這將告知這實際上是ajax調用。您可以在下面看到重寫的AjaxTilesView類,示例配置和jquery調用的示例。

AjaxTilesView。Java的:

import java.util.HashMap; 
import java.util.HashSet; 
import java.util.Iterator; 
import java.util.Map; 
import java.util.Set; 

import javax.servlet.ServletContext; 
import javax.servlet.ServletException; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import org.apache.tiles.Attribute; 
import org.apache.tiles.AttributeContext; 
import org.apache.tiles.Definition; 
import org.apache.tiles.access.TilesAccess; 
import org.apache.tiles.impl.BasicTilesContainer; 
import org.apache.tiles.request.ApplicationContext; 
import org.apache.tiles.request.Request; 
import org.apache.tiles.request.jsp.JspRequest; 
import org.apache.tiles.request.servlet.ServletRequest; 
import org.apache.tiles.request.servlet.ServletUtil; 
import org.springframework.js.ajax.AjaxHandler; 
import org.springframework.js.ajax.SpringJavascriptAjaxHandler; 
import org.springframework.util.Assert; 
import org.springframework.util.StringUtils; 
import org.springframework.web.servlet.support.JstlUtils; 
import org.springframework.web.servlet.support.RequestContext; 
import org.springframework.web.servlet.view.tiles3.TilesView; 

public class AjaxTilesView extends TilesView { 

    private static final String FRAGMENTS_PARAM = "fragments"; 

    private ApplicationContext applicationContext; 

    private AjaxHandler ajaxHandler = new SpringJavascriptAjaxHandler(); 

    public void afterPropertiesSet() throws Exception { 
     super.afterPropertiesSet(); 
    } 

    public AjaxHandler getAjaxHandler() { 
     return ajaxHandler; 
    } 

    public void setAjaxHandler(AjaxHandler ajaxHandler) { 
     this.ajaxHandler = ajaxHandler; 
    } 

    protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) 
      throws Exception { 


     ServletContext servletContext = getServletContext(); 
     if (ajaxHandler.isAjaxRequest(request, response)) { 
      String[] fragmentsToRender = getRenderFragments(model, request, response); 
      if (fragmentsToRender.length == 0) { 
       logger.warn("An Ajax request was detected, but no fragments were specified to be re-rendered. " 
         + "Falling back to full page render. This can cause unpredictable results when processing " 
         + "the ajax response on the client."); 
       super.renderMergedOutputModel(model, request, response); 
       return; 
      } 

      this.applicationContext = ServletUtil.getApplicationContext(getServletContext()); 
      BasicTilesContainer container = (BasicTilesContainer) TilesAccess.getContainer(this.applicationContext); 
      if (container == null) { 
       throw new ServletException("Tiles container is not initialized. " 
         + "Have you added a TilesConfigurer to your web application context?"); 
      } 

      exposeModelAsRequestAttributes(model, request); 
      JstlUtils.exposeLocalizationContext(new RequestContext(request, servletContext)); 
      Request tilesRequestContext = new ServletRequest(this.applicationContext, request, response); 
      Definition compositeDefinition = container.getDefinitionsFactory().getDefinition(getUrl(), 
        tilesRequestContext); 
      Map flattenedAttributeMap = new HashMap(); 
      flattenAttributeMap(container, tilesRequestContext, flattenedAttributeMap, compositeDefinition, request, 
        response); 
      addRuntimeAttributes(container, flattenedAttributeMap, request, response); 
      if (fragmentsToRender.length > 1) { 
       request.setAttribute(JspRequest.FORCE_INCLUDE_ATTRIBUTE_NAME, true); 
      } 

      for (int i = 0; i < fragmentsToRender.length; i++) { 
       Attribute attributeToRender = (Attribute) flattenedAttributeMap.get(fragmentsToRender[i]); 

       if (attributeToRender == null) { 
        throw new ServletException("No tiles attribute with a name of '" + fragmentsToRender[i] 
          + "' could be found for the current view: " + this); 
       } else { 
        container.startContext(tilesRequestContext).inheritCascadedAttributes(compositeDefinition); 
        container.render(attributeToRender, tilesRequestContext); 
        container.endContext(tilesRequestContext); 
       } 
      } 
     } else { 
      super.renderMergedOutputModel(model, request, response); 
     } 
    } 

    protected String[] getRenderFragments(Map model, HttpServletRequest request, HttpServletResponse response) { 
     String attrName = request.getParameter(FRAGMENTS_PARAM); 
     String[] renderFragments = StringUtils.commaDelimitedListToStringArray(attrName); 
     return StringUtils.trimArrayElements(renderFragments); 
    } 

    /** 
    * <p> 
    * Iterate over all attributes in the given Tiles definition. Every attribute value that represents a template (i.e. 
    * start with "/") or is a nested definition is added to a Map. The method class itself recursively to traverse 
    * nested definitions. 
    * </p> 
    * 
    * @param container the TilesContainer 
    * @param requestContext the TilesRequestContext 
    * @param resultMap the output Map where attributes of interest are added to. 
    * @param compositeDefinition the definition to search for attributes of interest. 
    * @param request the servlet request 
    * @param response the servlet response 
    */ 
    protected void flattenAttributeMap(BasicTilesContainer container, Request requestContext, 
      Map resultMap, Definition compositeDefinition, HttpServletRequest request, HttpServletResponse response) { 
     Set<String> locAttr = compositeDefinition.getLocalAttributeNames(); 
     Set<String> cascAttr = compositeDefinition.getCascadedAttributeNames(); 


     for (String s : locAttr) { 
      String attributeName = s; 
      Attribute attribute = compositeDefinition.getAttribute(attributeName); 
      if (attribute.getValue() == null || !(attribute.getValue() instanceof String)) { 
       continue; 
      } 
      String value = attribute.getValue().toString(); 
      if (value.startsWith("/")) { 
       resultMap.put(attributeName, attribute); 
      } else if (container.isValidDefinition(value, new ServletRequest(this.applicationContext, request, response))) { 
       resultMap.put(attributeName, attribute); 
       Definition nestedDefinition = container.getDefinitionsFactory().getDefinition(value, requestContext); 
       Assert.isTrue(nestedDefinition != compositeDefinition, "Circular nested definition: " + value); 
       flattenAttributeMap(container, requestContext, resultMap, nestedDefinition, request, response); 
      } 
     } 

     if(cascAttr == null) 
      return; 

     for (String s : cascAttr) { 
      String attributeName = s; 
      System.out.println(s); 
      Attribute attribute = compositeDefinition.getAttribute(attributeName); 
      if (attribute.getValue() == null || !(attribute.getValue() instanceof String)) { 
       continue; 
      } 
      String value = attribute.getValue().toString(); 
      if (value.startsWith("/")) { 
       resultMap.put(attributeName, attribute); 
      } else if (container.isValidDefinition(value, new ServletRequest(this.applicationContext, request, response))) { 
       resultMap.put(attributeName, attribute); 
       Definition nestedDefinition = container.getDefinitionsFactory().getDefinition(value, requestContext); 
       Assert.isTrue(nestedDefinition != compositeDefinition, "Circular nested definition: " + value); 
       flattenAttributeMap(container, requestContext, resultMap, nestedDefinition, request, response); 
      } 
     } 


    } 

    /** 
    * <p> 
    * Iterate over dynamically added Tiles attributes (see "Runtime Composition" in the Tiles documentation) and add 
    * them to the output Map passed as input. 
    * </p> 
    * 
    * @param container the Tiles container 
    * @param resultMap the output Map where attributes of interest are added to. 
    * @param request the Servlet request 
    * @param response the Servlet response 
    */ 
    protected void addRuntimeAttributes(BasicTilesContainer container, Map resultMap, HttpServletRequest request, 
      HttpServletResponse response) { 
     AttributeContext attributeContext = container.getAttributeContext(new ServletRequest(this.applicationContext, request, response)); 
     Set attributeNames = new HashSet(); 
     if (attributeContext.getLocalAttributeNames() != null) { 
      attributeNames.addAll(attributeContext.getLocalAttributeNames()); 
     } 
     if (attributeContext.getCascadedAttributeNames() != null) { 
      attributeNames.addAll(attributeContext.getCascadedAttributeNames()); 
     } 
     Iterator iterator = attributeNames.iterator(); 
     while (iterator.hasNext()) { 
      String name = (String) iterator.next(); 
      Attribute attr = attributeContext.getAttribute(name); 
      resultMap.put(name, attr); 
     } 
    } 
} 

的jQuery:

$('div[id="form"]').on("click",function(){ 
    $.ajax({ 
     type:"GET", 
     beforeSend: function (request) 
     { 
      request.setRequestHeader("Accept", "text/html;type=ajax"); 
     }, 
     url: "directlink?fragments=form", 
     processData: false, 
     success: function(msg) { 
      $('div[id="form"]').append(msg); 
      } 
    }); 
}); 

調度-config.xml中:

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer"> 
    <property name="definitions" value="WEB-INF/tiles-config.xml"/> 
</bean> 
<bean id="viewResolver" class="org.springframework.web.servlet.view.tiles3.TilesViewResolver" p:order="1"/> 
<bean id="ajaxViewResolver" class="org.springframework.js.ajax.AjaxUrlBasedViewResolver" p:order="0"> 
    <property name="viewClass" value="com.springframework.web.views.AjaxTilesView"/> 
</bean> 

如果有任何不喜歡頭添加到他們的jQuery,正如我之前提到的,你可以使用參數ajaxSource值不重要,但它必須有文字。因此示例url將如下所示:

'myurl?fragments=someTail&ajaxSource=on' 
'myurl?fragments=someTail&ajaxSource=placeholdertext' 
0

我想你錯過了瓷磚核心瓷磚api依賴關係。

tiles-api包含缺失的類。

<dependency> 
    <groupId>org.apache.tiles</groupId> 
    <artifactId>tiles-core</artifactId> 
    <version>3.0.1</version> 
</dependency> 

<dependency> 
    <groupId>org.apache.tiles</groupId> 
    <artifactId>tiles-api</artifactId> 
    <version>3.0.1</version> 
</dependency> 
+0

嗯,這不是這個,tiles-jsp依賴於這兩個(和更多)庫,它們被maven包含。只是爲了確定,我試圖用你的代碼片段手動添加這些依賴關係,但仍然得到了相同的結果NoClassDefFoundError – DonCziken

+0

但是它看起來像在tailes 3中沒有TilesApplicationContext這樣的類,或者它位於diffrent package cuz i不要在org.apache.tiles中看到它 – DonCziken