買了一本「王者歸來 Java Web 整合開發 (第二版)」,書中包含了 JSP + Servlet + Struts + Hibernate + Spring 等的教學及範例,K了一個星期後,發現最困難的部份竟然在環境架設上,光是環境架設就花了好長的時間,再來是設定檔,小蛙的 Eclipse 有些設定檔的 auto-complete 跑不出東西,加上每個版本的套件下載下來後的 Jar 檔、設定檔都不相同,不同版本的整合方式也不同 … 花了很久的時間在環境架設及設定檔上,這篇文章不記錄完整經過(忘的差不多了),只挑著把過程中要注意的事情及錯誤記錄下來。

  1. 隱藏 .jsp 頁面,對外以 .do 為主

    將 jsp 頁面通通放在 WEB-INF 底下,可自行建立其他分類資料夾,ex. pages, pages/admin, pages/member … 等。並且在 struts-conf.xml 中設定

    <action-mappings>
        <action path="/index" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/pages/index.jsp"></action>
    </action-mappings>
  2. java.lang ClassNotFoundException 各式各樣怪怪的情況

    這情況太容易出現了,小蛙原本使用 Eclipse IDE 裡面自訂 User Library 的功能,卻一直報錯,最後把所有會用到的 Spring, Struts Jar 通通放在 Web/WEB-INF/lib 下,這些怪裡怪氣的錯誤就自己消失了 …

  3. 亂碼處理

    這應該是最容易遇到的問題,有幾項前提要先遵守:

    R1.  必須先將每一頁 JSP 或每一個 Servelt 設定成 UTF-8 編碼(小蛙直接用 UTF-8 編碼,可以省掉 BIG5 的一些麻煩),如果是 Eclipse 專案請確定專案編碼為 UTF-8,有些文字編輯器可以看到目前檔案編碼。

    R2. 設定 JSP 表頭:
            <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%>

    R3. html meta:
            <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″>

    以上三個一定要指定同一編碼,確定之後接下來四種方法只要選一種來做就可以了:

    • 勤勞工人法:

      在每個 JSP 頁面及每個 Servelt 中設定

      request.setCharacterEncoding("UTF-8");
      response.setCharacterEncoding("UTF-8");
    • 源頭搞定法:

      使用 tomcat 預設過濾器 (在 Tomcat安裝目錄/conf/ web.xml 中解開註解)

      <filter>
          <filter-name>setCharacterEncodingFilter</filter-name>
          <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
          <init-param>
              <param-name>encoding</param-name>
              <param-value>UTF-8</param-value>
          </init-param>
          <async-supported>true</async-supported>
      </filter>
      <filter-mapping>
          <filter-name>setCharacterEncodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
    • 自我肯定法:

      自定義過濾器
      CharacterEncodingFilter.java

      package com.hans.filter;
      import java.io.IOException;
      import javax.servlet.Filter;
      import javax.servlet.FilterChain;
      import javax.servlet.FilterConfig;
      import javax.servlet.ServletException;
      import javax.servlet.ServletRequest;
      import javax.servlet.ServletResponse;
      public class CharacterEncodingFilter implements Filter {
      	private String characterEncoding;
      	@Override
      	public void init(FilterConfig config) throws ServletException {
      		characterEncoding = config.getInitParameter("encoding");
      	}
      	@Override
      	public void doFilter(ServletRequest request, ServletResponse response,
      			FilterChain chain) throws IOException, ServletException {
      		request.setCharacterEncoding(characterEncoding);
      		response.setCharacterEncoding(characterEncoding);
      		chain.doFilter(request, response);
      	}
      	@Override
      	public void destroy() {
      		characterEncoding = null;
      	}
      }

      web 下的 web.xml 加入對應

      <filter>
          <filter-name>CharacterEncodingFilter</filter-name>
          <filter-class>com.hans.filter.CharacterEncodingFilter</filter-class>
          <init-param>
              <param-name>encoding</param-name>
              <param-value>utf-8</param-value>
          </init-param>
      </filter>
      <filter-mapping>
          <filter-name>CharacterEncodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
    • 套件嵌用法:

      使用 Spring 過濾器

      <filter>
          <filter-name>CharacterEncodingFilter</filter-name>
          <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
          <init-param>
              <param-name>encoding</param-name>
                  <param-value>utf-8</param-value>
          </init-param>
          <init-param>
              <param-name>forceEncoding</param-name>
            	<param-value>true</param-value>
          </init-param>
      </filter>
      <filter-mapping>
          <filter-name>CharacterEncodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>

      以上幾種方法可以挑選適用於自己的方法來用,勤勞工人法是比較累一點,每一頁都要記得檢查,不然就可能在表單傳遞的時候發生亂碼。

  4. JNDI 錯誤

    javax.servlet.ServletException: javax.servlet.jsp.JspException: Unable to get connection, DataSource invalid: "org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'"
    javax.servlet.ServletException: org.apache.jasper.JasperException: javax.servlet.ServletException: javax.servlet.jsp.JspException: Unable to get connection, DataSource invalid: "org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'"

    明明設定上沒有什麼問題,卻發生以上 exception,設定檔如下(MySQL):

    <Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
    	   maxActive="5" maxIdle="2" maxWait="60"
    	   username="your_account" password="your_password" driverClassName="com.mysql.jdbc.Driver"
    	   url="jdbc:mysql://your_domain:3306/your_database?characterEncoding=UTF-8"/>

    不管設定在 server.xml 的 <GlobalNamingResources> 標籤中、設定在 <Host> 標籤下或是設定在 localhost/context.xml 下都會出現上面的錯誤,如果有遇到這樣問題的網友,試試看設定在 Tomcat安裝目錄/conf/context.xml 裡面就不會發生這個錯誤囉!如果用這個方法要把 mysql-connector-java-5.1.22-bin.jar 複製到 Tomcat安裝目錄/conf/ 資料夾下。

    在 Spring 中加入上述 DataSource,可以供其他部份使用(透過 id 指定)

    <!-- 吃 tomcat 內部設定的 jndi 不然就要自己設定 -->
    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:comp/env/jdbc/TestDB"></property>
    </bean>
  5. Struts 標籤設定

    前面提到書上的範例是寫

    <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %>
    <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %>

    Eclipse 頻頻出現錯誤訊息,後來才查到 1.3 版要改成以下才可以使用

    <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
    <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
  6. struts-conf.xml 錯誤

    如果使用 1.3 的表頭檔是沒辦法設定標籤的

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE struts-config
    	PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
    	"http://struts.apache.org/dtds/struts-config_1_3.dtd">

    要把它改成 1.2 版本的,並且要注意每個 tag 之間的順序,這邊出錯的時候 Eclipse 會列出順序,照著排就可以了。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE struts-config
    	PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
    	"http://struts.apache.org/dtds/struts-config_1_2.dtd">
  7. Action 中使用 getDataSource 錯誤

    書上的範例以及網路上查到的資料都說在 Struts Action 中,可以透過 getDataSource(request).getConnection(); 的方式來取得連接池的 Connection,實際上試了之後才會發現 1.3 根本就沒有 getDataSource 這個方法,繞了好久才找到必須透過以下方式取得剛剛設定在 context.xml 中的 datasource。

    DataSource ds = (DataSource)new InitialContext().lookup("java:comp/env/jdbc/TestDB");
    Connection conn = ds.getConnection();
  8. Spring, Struts 接合

    接著設定 struts-config.xml 加入 <controller><plug-in> 標籤,這邊家玩如果有 ClassNotFoundException 一樣參考這篇文章第 2 點說明,Eclipse 有報錯要注意 tag 間的順序。

    <controller processorClass="org.springframework.web.struts.DelegatingRequestProcessor"></controller>
    <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
        <set-property value="/WEB-INF/beans-conf.xml" property="contextConfigLocation"/>
    </plug-in>

    原本 struts-config.xml 中的 action 設定如下

    <action-mappings>
        <!-- 這種是預設到特定頁面的,為了隱藏 jsp -->
        <action path="/index" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/pages/index.jsp"></action>
        <!-- 接合 Spring 後的寫法,注意 action path (struts) 要跟 bean name (spring) 一致-->
        <action path="/hello2">
            <forward name="helloUser" path="/WEB-INF/pages/footer.jsp"></forward>
        </action>
    </action-mappings>

    Spring 設定 beans-conf.xml 如下 (範例參考自良葛格學習筆記,userChecker 及 HelloAction 請參考 良葛格學習筆記)

    <bean id="userChecker" class="com.hans.pojo.UserChecker"></bean>
    <!-- bean name 要跟 struts 裡面設定的 action path 一致 -->
    <bean name="/hello2" class="com.hans.struts.action.HelloAction2">
        <property name="userChecker" ref="userChecker" />
    </bean>
  9. Hibernate – getHibernateTemplate().find() 錯誤
    小蛙讓 MemberDao 繼承 HibernateDaoSupport 操作資料庫上比較方便,但透過 HibernateTemplate().find() 查找資料的時候(程式碼如下)

    List<Member> list = this.getHibernateTemplate().find("from Member member");
    if(list.size() > 0)
        return list.get(0);

    出現以下錯誤

    javax.servlet.ServletException: org.springframework.orm.hibernate3.HibernateQueryException: unexpected token: member near line 1, column 29 [from com.hans.entity.Member member]; nested exception is org.hibernate.hql.ast.QuerySyntaxException: unexpected token: member near line 1, column 29 [from com.hans.entity.Member member]

    from Member member 改成 from member,出現下面錯誤

    javax.servlet.ServletException: org.springframework.orm.hibernate3.HibernateQueryException: unexpected token: member near line 1, column 6 [from member]; nested exception is org.hibernate.hql.ast.QuerySyntaxException: unexpected token: member near line 1, column 6 [from member]

    改成 from Member 之後才可正常執行,後來在這篇文章看到原來 … member 是保留字 … 難怪怎麼試都會錯,還以為自己哪裡有問題。

參考資料:

  1. Struts+Spring+Hibernate项目框架构建中的问题及解决方法集锦(1) @ Morecans的专栏
    http://blog.csdn.net/morecans/article/details/1686416
  2. Spring Gossip: 在 Struts 中整合 Spring @ 良葛格學習筆記
    http://caterpillar.onlyfun.net/Gossip/SpringGossip/StrutsSpring.html 
  3. Hibernate 查詢數據時報錯,請問這是什麽原因呢?急死了! @ [email protected]
    http://www.javaworld.com.tw/jute/post/view?bid=41&id=171839

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *