我們經常為每一個Action撰寫一個Controller類別,很快的,您發現到您的Web應用程式下越來越多的Controller類別,而事實上當中的方法是可以組織在一起成為一個類別檔案,例如與資料庫操作相關的list()、add()、delete()等方法,就可以組織為一個類別。
要將相關的方法組織在一個檔案中,只使用一個handleRequest()方法似乎是不夠的,我們希望同一個Controller中可以根據我們的指定來Action中的某個方法,而不只是使用handleRequest()方法。
作為org.springframework.web.servlet.mvc.AbstractController下的另一個子類別:org.springframework.web.servlet.mvc.multiaction.MultiActionController,它可以讓我們在一個Controller類別中定義多個方法,並根據我們的請求來呼叫當中的某個方法來執行。
如果您瞭解Struts,其DispatchAction就類似於Spring的MultiActionController類別,所不同的是,MultiActionController擁有比Struts的DispatchAction更多樣化的方式來參數化與組織這些Action。
要使用MultiActionController,您要配合一個MethodNameResolver的實例,MultiActionController預設是InternalPathMethodNameResolver,這是最簡單的方式,根據您所給的網址最後的檔案名稱來判斷要執行Action中的哪一個方法,但通常我們不會使用InternalPathMethodNameResolver,因為這就失去了使用MultiActionController的一些優點,像是依請求參數來決定要呼叫的方法。
我們通常會使用ParameterMethodNameResolver或PropertiesMethodNameResolver,在一個網址上結合請求參數,以參數來決定要執行哪一個Action,這邊以ParameterMethodNameResolver為例,首先,我們可以在bean定義檔中這麼撰寫:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/book.do">bookAction</prop>
</props>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.InternalResourceView</value>
</property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="paraMethodResolver"
class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
<property name="paramName"><value>method</value></property>
<property name="defaultMethodName"><value>list</value></property>
</bean>
<bean id="bookAction" class="onlyfun.caterpillar.GuestBookAction">
<property name="methodNameResolver">
<ref bean="paraMethodResolver"/>
</property>
<property name="testPage">
<value>test</value>
</property>
</bean>
</beans>
在paraMethodResolver中,我們使用paramName定義在http請求中使用method參數來指定要呼叫的方法,若是請求中沒有指定method參數,則會使用defaultMethodName所指定的方法,這邊指定的是list()方法。
GuestBookAction是個繼承MultiActionController的類別,當中我們可以定義我們在method參數指定下所要呼叫的方法,例如:
package onlyfun.caterpillar;
import javax.servlet.http.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
public class GuestBookAction extends MultiActionController {
privateString testPage;
public ModelAndView list(HttpServletRequest req, HttpServletResponse res) {
returnnew ModelAndView(this.getTestPage(),"executed", "list");
}
public ModelAndView add(HttpServletRequest req, HttpServletResponse res) {
returnnew ModelAndView(this.getTestPage(),"executed", "add");
}
public ModelAndView delete(HttpServletRequest req, HttpServletResponse res) {
returnnew ModelAndView(this.getTestPage(),"executed", "delete");
}
publicString getTestPage() {
return testPage;
}
public void setTestPage(String testPage) {
this.testPage = testPage;
}
}
注意到我們的方法必須包括HttpServletRequest與HttpServletResponse作為參數,您也可以使用帶有第三個參數HttpSession的版本。
上面的類別只是一個簡單的範例,用於測試MultiActionController的運作,我們測試用的JSP網頁如下:
<html>
<head><title>Test</title></head>
<body>
<H1> "${executed}" method is executed.</H2>
</body>
</html>
當您使用以下的網址請求資源時:
http://localhost:8080/springapp/book.do?method=add
我們的測試網頁將傳回以下的內容:
<html>
<head><title>Test</title></head>
<body>
<H1> "add" method is executed.</H2>
</body>
</html>
您也可以將所有相對應的方法專門組織在一個委託(delegate)物件中,而不是撰寫在Controller類別中,當請求來到時,將委託給這個物件來執行指定的方法,您只要定義MultiActionController的delegate屬性即可,例如我們的委託物件如下:
package onlyfun.caterpillar;
import javax.servlet.http.*;
import org.springframework.web.servlet.ModelAndView;
public class BookActionDelegate {
privateString testPage;
public ModelAndView list(HttpServletRequest req, HttpServletResponse res) {
returnnew ModelAndView(this.getTestPage(),"executed", "list");
}
public ModelAndView add(HttpServletRequest req, HttpServletResponse res) {
returnnew ModelAndView(this.getTestPage(),"executed", "add");
}
public ModelAndView delete(HttpServletRequest req, HttpServletResponse res) {
returnnew ModelAndView(this.getTestPage(),"executed", "delete");
}
publicString getTestPage() {
return testPage;
}
public void setTestPage(String testPage) {
this.testPage = testPage;
}
}
現在我們不用定義GuestBookAction了,直接使用MultiActionController,只要定義Bean定義檔就好了:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/book.do">bookAction</prop>
</props>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.InternalResourceView</value>
</property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="paraMethodResolver"
class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
<property name="paramName"><value>method</value></property>
<property name="defaultMethodName"><value>list</value></property>
</bean>
<bean id="bookActionDelegate" class="onlyfun.caterpillar.BookActionDelegate">
<property name="testPage">
<value>test</value>
</property>
</bean>
<bean id="bookAction" class="org.springframework.web.servlet.mvc.multiaction.MultiActionController">
<property name="methodNameResolver">
<ref bean="paraMethodResolver"/>
</property>
<property name="delegate">
<ref bean="bookActionDelegate"/>
</property>
</bean>
</beans>
測試的結果與上面是相同的。
以上說明的是ParameterMethodNameResolver,而PropertiesMethodNameResolver的設定方法是類似的,有興趣的話,可以看一下Spring參考手冊中有關MultiActionController的說明,當中也有個簡明的設定範例。