SpringMVC學習

一、SpringMVC

SpringMVC是一種基於Java的實現了Web MVC設計模式的請求驅動類型的輕量級Web框架,即使用了MVC架構模式的思想,將web層進行職責解耦,基於請求驅動指的就是使用請求-響應模型。

SpringMVC前端控制器是DispatcherServlet,應用控制器其實拆爲處理器映射器(Handler Mapping)進行處理器管理和試圖解析器(View Resolver)進行試圖管理;頁面控制器/動作/處理器爲Controller接口的實現;支持本地化解析、主題解析及文件上傳等;提供了非常靈活的數據驗證、格式化和數據綁定製,提供了強大的約定大於配置的契約式編程支持。

(一)SpringMVC能幫我們做什麼

  • 讓我們能非常簡單的設計出乾淨的web層和薄薄的web層,進行更簡潔的web層開發
  • 天生與Spring框架集成
  • 提供強大的約定大於配置的契約式編程支持
  • 能簡單的進行web層的單元測試
  • 支持靈活的URL到頁面控制器的映射
  • 非常容易與其他試圖集成,如Velocity、FreeMarker等等,因爲模型數據不放在特定的API裏,而是放在一個Model裏(Map數據結構實現,因此很容易被其他框架使用)
  • 非常靈活的數據驗證、格式化和數據綁定機制,能使用任何對象進行數據綁定,不必實現特定框架的API
  • 提供一套強大的JSP標籤庫,簡化JSP開發
  • 支持靈活的本地化、主題等解析
  • 更加簡單的異常處理
  • 對靜態資源的支持
  • 支持Restful風格

SpringMVC框架也是一個基於請求驅動的Web框架,並且也使用了前端控制器模式來進行設計,再根據請求映射規則發給響應的頁面控制器進行處理。

SpringMVC處理請求的流程:

(二)SpringMVC的使用流程

SpringMVC提供一個公共的Servlet:根據請求動態獲取此次請求要調用的控制器對象和單元方法。給每個控制器取個別名,控制器的單元方法也都取個別名,在服務器啓動的時候我們會創建Spring容器的子容器對象。子容器對象就會完成控制器對象的初始化創建,以及所有單元方法的掃描。生成一個Map集合,並將map集合賦值給Servlet的全局屬性。

(1)導入jar包

commons-logging-1.1.3.jar
spring-aop-4.1.6.RELEASE.jar
spring-beans-4.1.6.RELEASE.jar
spring-context-4.1.6.RELEASE.jar
spring-core-4.1.6.RELEASE.jar
spring-expression-4.1.6.RELEASE.jar
spring-web-4.1.6.RELEASE.jar
spring-webmvc-4.1.6.RELEASE.jar

(2)配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--省略命名空間 -->
    <!--配置Spring的配置文件路徑  -->
    <!--配置監聽器 -->
    <!-- 配置SpringMVC的Servlet -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置SpringMVC容器的配置文件路徑 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
  	</servlet-mapping>
</web-app>

(3)配置springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--省略命名空間 -->
        <!-- 聲明註解掃描 -->
        <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
        <!-- 聲明MVC註解驅動解析器 -->
        <mvc:annotation-driven></mvc:annotation-driven>
        <!-- 配置靜態資源放行 -->
        <mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
        <mvc:resources location="/css/" mapping="/css/**"></mvc:resources>	
        <mvc:resources location="/images/" mapping="/images/**"></mvc:resources>
</beans>

(4)創建控制器類並聲明單元方法,以及給單元方法使用註解配置別名

@Controller
public class MyCon {	
	// 單元方法
	@RequestMapping("demo")
	public String demo(){
		System.out.println("MyCon.demo()");
		return "index.jsp";
	}
}

(5)啓動服務器發起請求

    localhost:8080/虛擬項目名/單元方法別名

二、控制器單元方法的傳參

控制器:聲明瞭servlet要調用的方法的類

單元方法:控制器中聲明的被servlet調用處理請求的方法

流程:瀏覽器-->請求-->服務器-->DispactherServlet-->獲取請求中附帶的單元方法的別名-->根據別名獲取單元方法對象-->執行單元方法。

(一)緊耦方式

		/**
		 * 緊耦方式
		 * 	使用: 直接在單元方法上聲明形參  HttpServletRequest req
		 * 	特點: DispactherServlet會將此次請求的req對象作爲實參傳遞給調用的單元方法
		 * 	缺點: 需要程序員自己編寫代碼獲取req中封存的請求數據。
		 */
		@RequestMapping("demo1")
		public  String demo1(HttpServletRequest req){
			//獲取請求數據
			String uname=req.getParameter("uname");
			int age=Integer.parseInt(req.getParameter("age"));
			System.out.println("MyCon.demo1()"+uname+age);
			return "index.jsp";
		}

(二)解耦方式

1.形參名和請求數據的健名一致

		/**
		 * 	使用:
		 * 		在單元方法上聲明形參,形參名和請求數據的鍵名一致。
		 * 	特點:
		 * 		DispactherServlet在調用單元方法時會根據單元方法的形參名去獲取此次請求
		 * 		的request對象中的請求數據,並將獲取的數據作爲實參傳遞給單元方法。
		 * 	優點:
		 * 		DispactherServlet會根據形參的類型將請求數據強制轉換後傳遞。
		 */
		@RequestMapping("demo2")
		public  String demo2(String uname,int age){
			System.out.println("MyCon.demo2(解耦方式一:)"+uname+age);
			return "index.jsp";
		}

2.形參名和請求數據的健名不一致

	/**
	 * 解耦方式二:請求中的鍵名和單元方法的形參名不一致
	 * 	使用:如果單元方法的形參名和請求的鍵名不一致,則在形參中使用註解@RequestParam("請求數據鍵名")	
	 * 		的方式來獲取請求數據。
	 * 	內容
	 * 		簡寫方法:@RequestParam("請求數據鍵名")
	 * 		官方寫法
	 * 			@RequestParam(value="請求數據鍵名",defaultValue="",required=true)
	 * 			defaultValue:默認值
	 * 			required:
	 * 			    false:默認值,此次請求中可以沒有該形參的值。
	 * 			    true:此次請求必須給該形參賦值,否則400
	 */
	@RequestMapping("demo3")
	public String demo3(@RequestParam(value="uname2",defaultValue="hhh",required=true)String uname,int age){
		System.out.println("MyCon.demo2(解耦方式二:)"+uname+age);
		return "index.jsp";
	}	

3.使用實體類類型的形參

	/**
	 * 解耦方式三:可以聲明一個實體類類型的形參,接收封裝好的對象
	 * 	使用:聲明一個實體類,該類的屬性名必須和請求的鍵名一致,在單元方法上聲明實體類類型的形參即可
	 */
	@RequestMapping("demo4")
	public  String demo4(User u,String uname){
		System.out.println("MyCon.demo2(解耦方式三:)"+u.getUname()+u.getAge()+uname);
		return "index.jsp";
	}	

4.獲取同鍵不同值得請求數據

	/**
	 * 解耦方式四:獲取同鍵不同值的請求數據
	 *  使用:
	 *  	在單元方法中聲明String數組類型的形參,形參名爲同鍵不同值的鍵。
	 *  	在單元方法中聲明ArrayList<String> 集合,但是需要使用@RequestParam("請求鍵名")獲取請求數據。
	 */
	@RequestMapping("demo5")
	public  String demo5(String uname,int age,@RequestParam("fav")ArrayList<String> fav){
		System.out.println("MyCon.demo2(解耦方式四:)"+uname+age+fav.get(0));
		return "index.jsp";
	}

5.restful格式的請求地址及其請求數據

	/**
	 *restful格式的請求地址及其請求數據
	 *  傳統方式的請求地址及請求數據:
	 *  		localhost:8080/project/demo6?uname=zhangsan&age=18
	 *  restful格式的請求地址及其請求數據:
	 *  		localhost:8080/project/demo6/zhangsan/lisi
	 *  restful格式的特點:
	 *  	①該格式支持get請求方式
	 *  	②該格式將請求數據以請求地址的形式發送給服務器,而不是鍵值對。
	 *  技能點:
	 *  	①在@RequestMapping中聲明的單元方法的別名可以使用{鍵名}進行通用聲明
	 *  	②可以在單元方法的形參聲明中使用註解@PathVariable("鍵名")獲取請求地址中的數據。
	 *  總結:
	 *  	所謂restful風格的請求,說白了就是將請求數據聲明在了請求地址信息中,然後在後臺
	 *  	中聲明對應的佔位,然後將請求數據從路徑中切割出來,然後賦值給單元方法的形參
	 */
	@RequestMapping("demo6/{a1}/{b1}")
	public  String demo6(@PathVariable("a1")String uname,@PathVariable("b1")String age){
		System.out.println("MyCon.demo2(解耦方式五:)"+uname+age);
		return "index.jsp";
	}

三、控制器單元方法的響應處理

響應方式:請求轉發、重定向、直接響應

執行流程:服務器接受到請求後會調用DispactherServlet處理請求,而DispactherServlet會根據請求動態的調用對應的單元方法,在單元中聲明瞭請求處理代碼,單元方法被執行完畢後繼續執行DispactherServlet,由DispactherServlet根據單元方法的執行結果來響應處理結果。

響應的實現:以單元方法返回值的形式。因爲Servlet不是我們編寫的,但是我們又要告訴Servlet如何進行處理結果的響應,可以以單元方法的返回值的形式告訴DispactherServlet是該請求轉發還是重定向。

返回值的內容:請求轉發或者重定向的標識符以及資源路徑。(foward:index.jsp)(redirect:index.jsp)

返回值的類型:String類型、View接口類型、ModelAndView類型

@Controller
public class MyConReturn {
	//返回值類型爲String類型
		//請求轉發
		@RequestMapping("mf")
		public String myForward(HttpServletRequest req,String uname,int age){
			//使用request作用域存儲數據
			req.setAttribute("uname", uname);
			//響應
			return "forward:/findex.jsp";
		}
		//重定向
		@RequestMapping("mr")
		public String myRedirect(HttpServletRequest req,String uname,int age){
			//將數據存儲到session中
			HttpSession ss = req.getSession();
			ss.setAttribute("age",age);
			//響應
			return "redirect:/rindex.jsp";
		}
		//直接響應
		@RequestMapping("mp")
		public void myResp(HttpServletResponse resp) throws IOException{
			//直接響應
			resp.getWriter().write("today is a good day, To study is nice");
		}
	//返回值類型爲View接口類型
		/**
		 * 		①View v=new InternalResourceView("/findex.jsp");
		 * 		作用:存儲了請求轉發的路徑。
		 * 		②View v5=new RedirectView("/27-SpringMVC-return/rindex.jsp");
		 * 		作用:存儲了重定向的路徑
		 * 		總結:
		 * 			將路徑存儲到View接口的不同的實例化對象中,來區分是請求轉發還是重定向。
		 * 			將其返回給DispactherServlet使用 
		 */
		@RequestMapping("mv")
		public View testView(String name,int age){
			//請求轉發
				View v=new InternalResourceView("/findex.jsp");
			//重定向
				View v5=new RedirectView("/27-SpringMVC-return/rindex.jsp");
			return v5;
		}
	//返回值類ModelAndView
		/**
		 * ModelAndView是SpringMVC繼View類型返回的升級版
		 * 	使用:
		 * 		支持字符格式
		 * 		支持View類型
		 * 	作用:
		 * 		根據存儲的資源類型來判斷請求轉發和重定向。
		 * 	Model對象的特點
		 * 		Model對象可以存儲作用域數據,類似request.
		 * 		但是在重定向的第二次請求會將其存儲的基本類型數據以請求數據的方式
		 * 		再次發給服務器,而不是第一次請求結束就銷燬。
		 */
		@RequestMapping("mav")
		public ModelAndView testModelAndView(Model m,String uname,int age){
			//請求轉發
				ModelAndView mv=new ModelAndView("forward:/findex.jsp");
				m.addAttribute("uname",uname);
			//重定向
				ModelAndView mv2=new ModelAndView("redirect:/rindex.jsp");
			return mv2;
		}

四、自定義視圖解析器

默認視圖解析器:我們在單元方法中直接返回字符串數據給DispachterServlet讓其解析返回的字符串數據,來完成請求轉發和重定向。

問題:在web項目中在webContent文件夾下除WEB-INF文件夾以外的所有資源對瀏覽器是可見的。也就是隻要我們知道該文件的資源路徑,在瀏覽器中就可以直接訪問,非常的不安全。

解決:因爲web-inf文件夾對瀏覽器是不可見的,所以我們可以將重要的靜態資源文件放到web-inf文件夾下。這樣瀏覽器就算知道地址也無法直接訪問,但是可以使用請求轉發的方法來間接的訪問web-inf下的資源。

實現:在單元方法的返回值,使用請求轉發WEB-INF下的資源,return "forward:/WEB-INF/資源路徑"

問題:在請求轉發WEB-INF下的資源的返回值的路徑書寫起來過於麻煩,而且因爲使用的地方多了會造成不好修改。

解決:使用自定義試圖解析器。

<?xml version="1.0" encoding="UTF-8"?>
<!--省略命名空間 -->
        <!--聲明註解掃描  -->
        <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
        <!--聲明MVC註解驅動解析器  -->
        <mvc:annotation-driven></mvc:annotation-driven>
        <!--配置靜態資源放行  -->
        <mvc:resources location="/js/" mapping="/js/**"></mvc:resources>	
        <mvc:resources location="/css/" mapping="/css/**"></mvc:resources>	
        <mvc:resources location="/images/" mapping="/images/**"></mvc:resources>	
        <!--配置自定義視圖解析器  -->
        <bean id="resourceViewResolver" 
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        	<property name="prefix" value="/WEB-INF/pages/"></property>
        	<property name="suffix" value=".jsp"></property>
        </bean>	
</beans>
@Controller
public class MyConSelf {
	//默認視圖解析器
		@RequestMapping("ms")
		public String mySelf(){
			System.out.println("MyConSelf.mySelf()");
				//請求轉發Jsp
					//return "forward:/index.jsp";
			//請求轉發其他單元方法
			return "forward:ms2";
		}
		@RequestMapping("ms2")
		public String mySelf2(){
			System.out.println("MyConSelf.mySelf2()");
			return "forward:/index.jsp";
		}
	//自定義視圖解析器
		@RequestMapping("ms3")
		public String mySelf3(){
			System.out.println("MyConSelf.mySelf()");
				//請求轉發Jsp
					//return "forward:/index.jsp";
			//請求轉發其他單元方法
			return "main";
		}
}

注意:在Springmvc的配置文件中配置了自定義視圖解析器後,如果單元方法的返回值中沒有forward,則會被自定義視圖解析器給返回值添加了前綴和後綴後再轉給DispactherServlet,如果聲明瞭forward,則會走默認試圖解析器,直接根據返回值進行資源的請求轉發。

五、SpringMVC的上傳下載

問題:SpringMVC將Service進行了封裝後,變成了公共的。以前我們是將上傳下載的代碼直接聲明在servlet的,那麼在使用SpringMVC之後怎麼辦呢?

解決:使用SpringMVC的上傳下載。

(一)所需jar包

aopalliance.jar
asm-3.3.1.jar
aspectjweaver.jar
cglib-2.2.2.jar
commons-fileupload-1.3.2.jar
commons-io-2.5.jar
commons-logging-1.1.1.jar
commons-logging-1.1.3.jar
javassist-3.17.1-GA.jar
jstl-1.2.jar
log4j-1.2.17.jar
log4j-api-2.0-rc1.jar
log4j-core-2.0-rc1.jar
mybatis-3.2.7.jar
mybatis-spring-1.2.3.jar
mysql-connector-java-5.1.30.jar
slf4j-api-1.7.5.jar
slf4j-log4j12-1.7.5.jar
spring-aop-4.1.6.RELEASE.jar
spring-aspects-4.1.6.RELEASE.jar
spring-beans-4.1.6.RELEASE.jar
spring-context-4.1.6.RELEASE.jar
spring-core-4.1.6.RELEASE.jar
spring-expression-4.1.6.RELEASE.jar
spring-jdbc-4.1.6.RELEASE.jar
spring-tx-4.1.6.RELEASE.jar
spring-web-4.1.6.RELEASE.jar
spring-webmvc-4.1.6.RELEASE.jar
standard-1.1.2.jar

(二)springmvc.xml配置上傳解析bean

<?xml version="1.0" encoding="UTF-8"?>
<!--省略命名空間 -->
        <!--配置註解掃描 -->
        <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
        <!--配置註解解析器  -->
       	<mvc:annotation-driven></mvc:annotation-driven>
        <!--配置靜態資源放行  -->
        <mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
        <mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
        <mvc:resources location="/images/" mapping="/images/**"></mvc:resources>
        <!--配置自定義視圖解析器  -->
        <!--配置上傳解析bean-->
        <bean id="multipartResolver" 
            class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        	<property name="defaultEncoding" value="utf-8"></property><!-- 設置解析編碼格式 -->
                <!--
                    在java代碼中photo.getSize()來對上傳文件的大小進行校驗也可以,
                    但是該校驗是發生在解析後的,其實大小的校驗在解析時就可以完成。
                    在spingmvc.xml文件中做如下配置
                -->
    		<property name="maxInMemorySize" value="10485760"></property><!-- 設置上傳數據的總大小 字節-->
    		<property name="maxUploadSize" value="1024128392"></property><!--設置單個文件大小 字節-->
        </bean>
        <!--配置指定異常跳轉備bean  -->
        <bean id="exceptionResolver" 
            class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
            <property name="exceptionMappings" >
                <props>
        	<prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">limit.jsp</prop>
        	</props>
            </property>
        </bean>
     </beans>

(三)web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<!--省略命名空間 -->
 	<!--Spring的配置文件路徑  -->
  		<context-param>
  			<param-name>contextConfigLocation</param-name>
  			<param-value>classpath:applicationcontext.xml</param-value>
  		</context-param>
  	<!--Spring的監聽器  -->
  		<listener>
  			<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  		</listener>
 <!-- 配置DispactherServlet -->
 	<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:springmvc.xml</param-value>
 		</init-param>
 		<load-on-startup>1</load-on-startup>
 	</servlet>
 	<servlet-mapping>
 		<servlet-name>mvc</servlet-name>
 		<url-pattern>/</url-pattern>
 	</servlet-mapping>
 <!-- 配置編碼過濾器 -->
 	<filter>
 		<filter-name>code</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>
 	</filter>
 	<filter-mapping>
 		<filter-name>code</filter-name>
 		<url-pattern>/*</url-pattern>
 	</filter-mapping>
</web-app>

(三)上傳

@Controller
public class MyConUpload {
   /** 
    * 將上傳數據存儲到服務器硬盤中
    * @param uid 上傳的用戶ID
    * @param uname 上傳的用戶名
    * @param photo  封存了上傳文件所有相關數據的對象。
    * @return
    * @throws IOException 
    */
    @RequestMapping("upload")
    public String myUpload(int uid,String uname,MultipartFile photo,HttpServletRequest req) throws IOException{
    //創建動態的文件名
        //獲取文件的後綴名,photo.getOriginalFilename()獲取原始文件名
        String suffixName = photo.getOriginalFilename().substring(photo.getOriginalFilename().lastIndexOf("."));
        //校驗文件類型
        if(!(".jpg".equals(suffixName) ||".png".equals(suffixName) || ".bmp".equals(suffixName))){
            return "forward:/error.jsp";
        }
        //創建動態的文件名
        String name=UUID.randomUUID()+""+System.currentTimeMillis();
        //拼接新的文件名
        String newName=name+suffixName;
    //獲取動態的存儲路徑
        //動態的獲取項目根目錄下的絕對路徑
        String path=req.getServletContext().getRealPath("/images");
        File fpath=new File(path);
        if(!fpath.exists()){
            fpath.mkdirs();
        }
        //拼接數據存儲的絕對路徑
        File f=new File(fpath,newName);
        photo.transferTo(f);
        //將上傳記錄存儲到數據庫記錄表中(用戶ID,上傳文件原始名,上傳文件新名,時間,文件類型)
        int i = uploadServiceImpl.insUploadInfo(uid, photo.getOriginalFilename(),
                                         newName, photo.getContentType());
        if(i>0){
	    return "forward:/success";
        }else{
            return "forward:/fail.jsp";
        }
    }
    //查詢所有的上傳信息
    @RequestMapping("success")
    public String getUploadInfo(HttpServletRequest req){
        List<Upload> lp=uploadServiceImpl.selUploadInfoService();
        //將數據存儲到作用域中	
        req.setAttribute("lp",lp);
        return "forward:/success.jsp";
    }
}

(四)下載

@Controller
public class MyConDown {
    //下載圖片資源
    @RequestMapping("down")
    public void getImage(String newName,String contentType,HttpServletResponse resp,HttpServletRequest req) 
        throws IOException{
        //設置響應的數據的MIME類型
        resp.setContentType(contentType);
        //設置響應頭:告訴瀏覽器將接受的流數據另存爲存儲到客戶端,而不是直接解析
        String str1="文件名";
        String str2=new String(str1.getBytes("utf-8"), "iso-8859-1");
        resp.setHeader("Content-Disposition", "attachment;filename="+str2);
        //獲取要下載的資源的讀取流對象
        //獲取資源存儲路徑	
        String path=req.getServletContext().getRealPath("/images");
        //FileInputStream fs=new FileInputStream(new File(path,newName));
        //獲取輸出流
        ServletOutputStream os = resp.getOutputStream();
        os.write(FileUtils.readFileToByteArray(new File(path,newName)));
    }
}

(五)JSP頁面

1.upload.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
	<h3>SpringMVC之上傳</h3>
	<hr />
	<form action="upload" method="post" enctype="multipart/form-data">
		用戶ID:<input type="text" name="uid" value=""/><br />
		用戶名:<input type="text" name="uname"  value=""/><br />
		頭像:<input type="file" name="photo" /><br />
		<input type="submit" value="點擊上傳" />		
	</form>
</body>
</html>

2.success.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
	<h3>顯示所有的上傳記錄</h3>
	<hr />
	<table border="1px" cellpadding="10px" cellspacing="0px">
		<tr>
		    <td>上傳編號</td>
		    <td>用戶編號</td>
		    <td>原始名</td>
		    <td>存儲名</td>
		    <td>預覽</td>
		    <td>上傳時間</td>
		    <td>類型</td>
		    <td>操作</td>
		</tr>
		<c:forEach items="${lp}" var="p">
		    <tr>
			<td>${p.upid}</td>
			<td>${p.uid}</td>
			<td>${p.oldName}</td>
			<td>${p.newName}</td>
			<td><image src="images/${p.newName}" width="100px"/></td>
			<td>${p.uploadTime}</td>
			<td>${p.contentType}</td>
			<td>
			    <a href="down?newName=${p.newName}&contentType=${p.contentType}">下載</a>
			</td>
		    </tr>
		</c:forEach>
	</table>
</body>
</html>

六、SpringMVC攔截器

問題:我們之前學習了過濾器,我們知道,過濾器是攔截Servlet請求的。先過濾後Servlet,但是在使用了SpringMVC後,Servlet就只有一個了,這樣造成只要項目中配置了過濾器,就會攔截非JSP以外的所有請求,無法根據業務需求攔截指定的請求?

解決:

①目前責任鏈執行流程:請求-》過濾器-》DispactherServlet-》控制器單元方法

②解決方案流程:請求-》過濾器-》DispactherServlet-》攔截器-》控制器單元方法

作用:攔截單元方法,將Servlet傳遞給單元方法的數據進行預處理,將單元方法的返回值給Servlet之前進行預處理。

特點:①創建一個實現了攔截器接口的java類,②在springmvc.xml中配置攔截器bean,③在springmvc.xml中配置攔截器的攔截返回

(一)springmvc.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<!--省略命名空間 -->
        <!--配置註解掃描 -->
        <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
        <!--配置註解解析器  -->
       	<mvc:annotation-driven></mvc:annotation-driven>
        <!--配置靜態資源放行  -->
        <mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
        <mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
        <mvc:resources location="/images/" mapping="/images/**"></mvc:resources>
        <!--配置自定義視圖解析器  -->
        <!--配置上傳資源解析bean  -->
        <!--配置指定異常跳轉bean  -->
        <!--配置攔截器攔截範圍  -->
        	<mvc:interceptors>
        		<!--配置全局攔截  -->
        		<bean id="my2" class="com.bjsxt.interceptor.MyIn2"></bean>
        		 <!--配置局部攔截  -->
        		 <mvc:interceptor>
        			<!-- 配置攔截範圍  -->
        			<mvc:mapping path="/demo"/>
        			<mvc:mapping path="/demo1"/>
                                <!-- 配置攔截器bean  -->
        			<bean id="my" class="com.bjsxt.interceptor.MyIn"></bean>
        		</mvc:interceptor>	 
        	</mvc:interceptors>
   </beans>

(二)實現HandlerInterceptor接口

public class MyIn implements HandlerInterceptor{
    @Resource
    private MyCon myCon;
    /**
     * 作用:攔截單元方法,在單元方法之前執行,對數據進行預處理
     * 參數:Object arg2(HandleMethod類型) 該參數的實參中封裝了兩次請求要調用的單元方法Method對象
     *       可以實現在preHandle方法中調用一次此次請求的單元方法
     * 返回值:boolean false攔截此次請求不再繼續執行單元方法
     *                 true放行,繼續執行單元方法
     */
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object arg2) 
        throws Exception {
	//在攔截器中調用單元方法
	HandlerMethod hm=(HandlerMethod) arg2;
	hm.getMethod().invoke(myCon, "李四",20);
	return true;
    }
    /**
     * 作用:在單元方法之後執行,jsp之前,可以對單元方法跳轉的資源進行重新定義,對敏感數據進行和諧
     */
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, 
        ModelAndView mv) throws Exception {
        //獲取當次請求的單元方法的返回值
            System.out.println(mv.getViewName());
        //敏感詞彙過濾
        String str=(String) mv.getModel().get("str");
        if(str.contains("中國")){
            str=str.replaceAll("中國", "**");
        }
        mv.getModel().put("str",str);
        //修改返回值,改變資源跳轉
        //mv.setViewName("forward:/index222222.jsp");
    }
    /**
     * 作用:在最後執行,處理前面的方法的異常
     * 參數:Exception arg3 接受異常信息
     */
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, 
        Exception arg3)throws Exception {
        System.out.println("MyIn.afterCompletion(3):");
    }
}

注意:在SpringMVC.xml文件中配置在前面的攔截器爲外攔截器,配置在後面的攔截器爲內攔截器。

七、SpringMVC運行原理

(一)四大核心組件

DispatchServlet:servelt分發器,整個SpringMVC框架入口

HandlerMapping:尋找URL所請求的HandlerMethod,找@RequestMapper(),使用實現類DefaultAnnotationHandlerMapping實際工作

HandlerAdapter:實際調用HandlerMethod的組件,使用實現類AnnotationMethodHandlerAdapater

ViewResovler:試圖解析器,作用解析HandlerMethod返回值,把邏輯視圖轉換爲需要調用的物理視圖。自定義時InternalResourceViewResolver

(二)<mvc:annotation-driven/>

當配置了<mvc:annotation-driven/>時,實際上創建了上面實現類的<bean>對象

(三)可能使用的組件或接口或類

Controller:控制器類

HandlerMethod:控制器方法

View:視圖

Model:模型

ModelAndView:模型和視圖,SpringMVC所有HandlerMethod最終都會轉換成ModelAndView

HandlerInterceptor:攔截器

HandlerExceptionResolver:異常映射解析器

MultipartResolver:Multipart解析器

CharacterEncodingFilter:字符編碼過濾器

(四)時序圖

(五)文字敘述

當用戶發起請求後,執行DiapacherServlet,如果是JSP直接調用jsp頁面,如果不是JSP,DispactherServlet調用HandlerMapping判斷請求URL是否合法,如果不是URL不存在報錯,如果URL存在使用HandlerAdpater調用具體的HandlerMethod,當Handler執行完成後會返回ModelAndView,會被ViewResovler解析,調用具體的物理視圖,最終響應給客戶端瀏覽器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章