1. 程式人生 > >springmvc DispatcherServlet和攔截器詳解

springmvc DispatcherServlet和攔截器詳解

1,程式例項

把程式貼下來:web.xml

<servlet> <!--既然DispatcherServlet 寫在這說明 DispatcherServlet也是servlet-->
    <servlet-name>mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:config/spring-mvc.xml</param-value>
    </init-param>
  </servlet>
<servlet-mapping>
    <servlet-name>mvc</servlet-name>
    <url-pattern>/</url-pattern>  <!-- 不可以用 /* 來代替所有的路徑,這裡攔截的是所有的路徑,
故意的,一般不要這樣寫,而是要加上一些限制 -->
  </servlet-mapping>
<servlet>
    <servlet-name>ToLogin</servlet-name>
    <servlet-class>servlet.ToLogin</servlet-class>
  </servlet>
<servlet-mapping>
    <servlet-name>ToLogin</servlet-name>
    <url-pattern>/toLogin.do</url-pattern><!-- servlet的請求路徑-->
</servlet-mapping>

config包中的 spring-mvc.xml檔案的主要資訊:

	<!-- 避免 Dispatcher 攔截掉靜態資源,當靜態資源路徑出現時,交由DispatcherServlet
處理,不會找其他Controller ,而是交給 ResourceHttpRequestHandler 處理,從而找到靜態資源-->
    <mvc:resources location="/page/" mapping="/page/**"/> 
<!--配置攔截器, 多個攔截器,順序執行 ,路徑攔截的相同也是互不影響的。-->  
<mvc:interceptors>    
    <mvc:interceptor>    
       
<!-- 匹配的是url路徑, 如果不配置 或 /**,將攔截所有的Controller,這裡為做實驗,採取全攔截 -->
        <mvc:mapping path="/**" />  
        <bean class="interceptor.MyInterceptor"></bean>    
    </mvc:interceptor>  
  <!--  當設定多個攔截器時,先按順序呼叫preHandle方法,然後逆序呼叫每個攔截器的postHandle和afterCompletion方法-->  
</mvc:interceptors>

配置檔案就這些。

接下來的就是Java程式碼:

ToLogin:路徑是 /toLogin.do
public class ToLogin extends HttpServlet {

	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request,response);
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		 System.out.println("spring MVC 進入 ToLogin ");
		 request.getSession().setAttribute("name","ToLogin.do");
		 if(request.getCookies()!=null)
				for(Cookie c:request.getCookies()){
					System.out.println(c.getName()+" : "+c.getValue());
				}
		 request.getRequestDispatcher("/login.jsp").forward(request, response);
	}

}
MyInterceptor: 作用是攔截所有的請求Controller的路徑。
public class MyInterceptor extends HandlerInterceptorAdapter {
	/* preHandle是一開始就 執行的 方法,並由返回的結果決定--> 後面的 攔截器是否執行 */
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		System.out.println("preHandle 執行!!!");
		
		return false; // 為了說明效果,全部攔截下來
		
	}


	/* postHandle 是controller正常執行完成後,執行的方法。如果沒有正常執行完成,所有的postHandle 方法都不會執行 */
	/* 該方法在controller正常執行完成之後 才會執行,並且在 中央處理器 渲染頁面之前 執行。 */
	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("postHandle 執行!!!");
	}


	/* afterCompletion 是隻要該攔截器的preHandle 方法執行的結果為true ,該方法就執行 */
	/*
	 * afterCompletion 在所有的工作完成之後,且該該攔截器的 preHandle 返回的結果是 true ,該方法相當於
	 * try-catch-finally 中的finally ,用來釋放一些資源。
	 */
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("afterCompletion 執行!!!");
	}
}
ControllerOne:程式碼 跳轉到 index.jsp頁面
@Controller
public class ControllerOne {
	// @RequestMapping("/hello") 這兩種url的請求方式都是一樣的。一種是絕對路徑,一種是相對路徑
	//
	@RequestMapping("hello")
	public ModelAndView getHello(int num) {
		System.out.println("進入hello請求中");
		ModelAndView mv = new ModelAndView();
		mv.setViewName("/index.jsp");
		return mv;
	}
}

請求結果及說明

本專案名為 springMVC 。
1,請求為 :http://localhost:8080/springMVC/login.jsp

結果如上,所載入的圖片沒有顯示出來。檢視控制檯:

Mapped URL path [/page/**] onto handler 'org.springframework.web.servlet.resource.ResourceHttpRequestHandler'
說明配置檔案中的資訊,靜態資源資訊已經交給了ResourceHttpRequestHandler這個Controller處理,DispatcherServlet會將靜態資源交由它處理。
preHandle 執行!!!
說明攔截器起了作用,而雖然請求login.jsp頁面,但是有兩次請求,一次是請求login.jsp第二次是請求 <img alt="你好" src="page/pieBiz.jpg"/> 靜態資源,但是攔截器只攔截一次,且根據結果看,只是攔截了後面的靜態資源的請求,導致沒有加載出圖畫。原因也在上面的日誌輸出上顯示出來了,就是為靜態資源服務的ResourceHttpRequestHandler被攔截,從而不會被處理的原因。
下一個請求:
http://localhost:8080/springMVC/toLogin.do

其實控制檯上沒有出現報錯或警告資訊,這是為什麼呢?畢竟是沒有toLogin.do對應的Controller,那麼DispatcherServlet如何將請求資訊正確轉發呢?其實這麼想就錯了。上面已經提到了DispatcherServlet本身就是一個轉發的servlet,並根據路徑的匹配原則,最長路徑匹配優先匹配的原則,所以當碰到一個合適的servlet就會先執行該servlet,這裡就是ToLogin.java, 同樣原理,jsp就是特殊的servlet,所以會直接訪問到,根本不經過
DispatcherServlet。
下面解決靜態資源無法訪問到的問題:
判斷當前的處理器是否是:org.springframework.web.servlet.resource.ResourceHttpRequestHandler,如果是就說明這個請求其實是靜態資源的請求。就要放行,改後的程式碼如下(攔截其中):
@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		System.out.println("preHandle 執行!!!");
		String str=handler.getClass().getName();
		//判斷是否是靜態資源的處理器,是就放行
		if("org.springframework.web.servlet.resource.ResourceHttpRequestHandler".equals(str)){
			return true;
		}
		return false;
		
	}


上圖是資源的程式碼層級。
其中index.jsp 和 helloone.html檔案都是普通的檔案,只是展示一些東西。要詳細說明的是 login.jsp :
<body>
    <form action="login" method="post">
        姓名:  <input type="text" name="name" /><br/>
        密碼:  <input type="password" name="passwd" /><br/>
        <input type="submit" value="提交"/>
        <input type="reset" value="清空"/> 
    </form>
    <img alt="你好" src="page/pieBiz.jpg"/> //引用的靜態資源
  </body>

執行結果如下(圖片被顯示出來):

通過這個例子相信大家已經對springMVC 的攔截器和DispatcherServlet以及servlet,jsp有了進一步的瞭解。