先來上一張SpringMVC處理請求全過程。
該圖來自:http://jinnianshilongnian.iteye.com/blog/1594806
清晰的展示了從請求以響應spring在中間都做了些什麼事情,感覺畫得好就先放在這裏了。
1、新建項目:myframework;
2、添加spring框架依賴jar包,爲了簡單我這裏把所有依賴包都添加了到WEB-INF\lib目錄下,另外還要添加一個三方包commons-logging-api-1.1.jar。準備工作完成,
3、在web.xml中添加DispatchServlet,內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>myframework</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>dispatch</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化參數 -->
<init-param>
<!-- 指明servlet配置文件位置,spring將從此位置載入spring容器 -->
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/dispatch-servlet*.*
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置DispatcherServlet所需要攔截的 url -->
<servlet-mapping>
<servlet-name>dispatch</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
load-on-startup:表示啓動容器時初始化該Servlet;
init-param:指明瞭dispatch這個servlet配置文件位置爲: /WEB-INF/classes/dispatch-servlet*.* ,非必須的配置。
url-pattern:表示哪些請求交給Spring Web MVC處理, “/” 是用來定義默認servlet映射的。也可以如“.html”表示攔截所有以html爲擴展名的請求,這裏將攔截所有以.do接尾的請求。
自此請求已交給Spring Web MVC框架處理,我們還需要配置Spring的配置文件,若不配置init-param指定配置文件位置,將從默認位置WEB-INF/[DispatcherServlet的Servlet名字]-servlet.xml,加載配置文件。這裏我指定了文件位置,所以將會從/WEB-INF/classes/dispatch-servlet*.* 加載。注意這裏多加了一層classess目錄。
4、配置dispatch-servlet.xml,我的dispatch-servlet.xml源文件位置在src目錄下,編譯後文件位置在WEB-INF\classess目錄下。內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置SpringMVC的視圖渲染器, 讓其前綴爲:/ 後綴爲.jsp 將視圖渲染到/page/<method返回值>.jsp中 -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/pages/" p:suffix=".jsp">
</beans:bean>
</beans:beans>
5、創建Controller。
package com.ldz.demo.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// 創建模型跟視圖,用於渲染頁面。並且指定要返回的頁面爲hello頁面,結合dispatch-servlet.xml中的配置,可知道最終返回頁面爲:/pages/hello.jsp
ModelAndView mav = new ModelAndView("hello");
return mav;
}
}
進入一下步,將Controller類註冊到dispatch-servlet.xml中,讓spring容器進行管理。
6、在dispatch-servlet.xml中添加配置
<!--這裏必須以.do接尾,否則報404錯誤,因爲web.xml中配置了spring只攔截.do接尾的請求-->
<beans:bean name="/hello.do" class="com.ldz.demo.controller.HelloController" />
至此,springMVC基本架子已經搭建好了。發佈到tomcat中,啓動應用,訪問:
http://localhost:8080/myframework/hello.do
應用目錄結構圖
一點說明:
仔細點可以看到dispatch-servlet.xml中每個結點都有<beans
前綴。這樣的寫法不太符合平時的習慣,如果改成下面寫法如何呢?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- SpringMVC配置 -->
<!-- 通過component-scan 讓Spring掃描org.swinglife.controller下的所有的類,讓Spring的代碼註解生效 -->
<!-- <context:component-scan base-package="com.ldz.demo.controller"></context:component-scan> -->
<!-- 配置SpringMVC的視圖渲染器, 讓其前綴爲:/ 後綴爲.jsp 將視圖渲染到/page/<method返回值>.jsp中 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/pages/" p:suffix=".jsp">
</bean>
<bean name="/hello.do" class="com.ldz.demo.controller.HelloController" />
</beans>
去掉前綴後,啓動時就報錯了,提示找不到<beans
的定義 ,那肯定就是引用的schema有問題,對比和以前項目文件後發現
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
修改爲下面schema後,就不需要beans前綴,而且也能正常啓動了。
<beans xmlns="http://www.springframework.org/schema/beans"
Controller實現方式
實現一個Controller有很多種方式,特別在spring2.5以後支持註解後,大大的減化了xml配置的繁所工作。
出於學習目的先來了解2.5之前實現Controller的方法。
第一種,上文中提到的,從Controller類繼承,只需要實現下面個方法就行了。
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception;
實現代碼
@Override
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// 創建模型跟視圖,用於渲染頁面。並且指定要返回的頁面爲home頁面
ModelAndView mav = new ModelAndView(“hello”);
mav.addObject(“desc”, “HelloController!”);
return mav;
}
在dispatch-servlet.xml配置控件器
<bean name="/hello.do" class="com.ldz.demo.controller.HelloController" />
第二種,從AbstractController繼承
實現代碼
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// 創建模型跟視圖,用於渲染頁面。並且指定要返回的頁面爲home頁面
ModelAndView mav = new ModelAndView(“hello”);
mav.addObject(“message”, “HiAbstractController!,這是一個從AbstractController繼承的Controller”);
return mav;
}
在dispatch-servlet.xml配置控件器,
<bean name="/hi.do" class="com.ldz.demo.controller.HiAbstractController" />
響應方式
返回ModelAndView或直接通過response寫響應。像ajax請求時,不需要返回頁面視圖時,就通過response寫響應,之後直接return null;告訴DispatchServlet已經自己返回數據,不需要進行視圖渲染。
代碼實現:@Override
protected ModelAndView handleRequestInternal(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// 創建模型跟視圖,用於渲染頁面。並且指定要返回的頁面爲home頁面
arg1.setHeader(“Content-type”, “text/html;charset=UTF-8”);
//這句話的意思,是告訴servlet用UTF-8轉碼,而不是用默認的ISO8859
arg1.setCharacterEncoding(“UTF-8”);
arg1.getWriter().write(“這句話是直接在Controller裏面直接返回頁面的!!”);
// 如果想直接在該處理器/控制器寫響應 可以通過返回null告訴DispatcherServlet自己已經寫出響應了,不需要它進行視圖解析
return null;
}在寫響應之前,設置了響應頭和字符編碼,如果不寫的話返回內容亂碼。而且要寫在方法最前面。對於編碼設置,肯定有比這個現簡單有效的辦法,編碼問題留到以後再來講。
繼承AbstractController其他可用參數設置
繼承至AbstractController的控件器有以下屬性可設置,繼承至Controller的控制器沒有這些屬性。
1.強制請求方法類型,強制指定客戶端請求時使用GET或POST請求類型,如登錄、註冊類請求強制使用POST方式提交數據。
在dispatch-servlet.xml中controller設置以下參數:
<bean name="/hi.do" class="com.ldz.demo.controller.HelloController">
<property name="supportedMethods" value="POST"></property>
</bean>
2.當前請求的session前置條件檢查,如果當前請求無session將拋出HttpSessionRequiredException異常:
<bean name="/hi.do" class="com.ldz.demo.controller.HelloController">
<property name="requireSession" value="true"/>
</bean>
3.客戶端端緩存控制
<bean name="/hi.do" class="com.ldz.demo.controller.HelloController">
<!-- value>0,數字表示緩存秒數 ,value=0:表示不緩存,value<0表示響應頭什麼緩存信息都不加。-->
<property name="cacheSeconds" value="5" />
</bean>
請求效果圖,第一次從服務器返回,緊接第二次Size列顯示(form cache)
。說明是從緩存中獲取請求數據。
注:下面提到一些關於緩存控制的一些特殊情況:
- 1. 對於一般的頁面跳轉(如超鏈接點擊跳轉、通過js調用window.open打開新頁面都是會使用瀏覽器緩存的,在未過期情況下會直接使用瀏覽器緩存的副本,在未過期情況下一次請求也不發送);
- 2.對於刷新頁面(如按F5鍵刷新),會再次發送一次請求到服務器;
4.Last-Modified緩存機制
在繼承AbstractController類基礎上再實現LastModified接口即可。
public long getLastModified(HttpServletRequest request) {
if(lastModified == 0L) {
//TODO 此處更新的條件:如果內容有更新,應該重新返回內容最新修改的時間戳
lastModified = System.currentTimeMillis();
}
return lastModified;
}