之前一直想學 springMVC ,拖阿託的沒學成,後來項目裏用了,但那是架構師已經搭建好的,也就沒有再去學,想着會用就能夠了。事實證實,我仍是太年輕,雖然會用就好,可是,面試官會問啊 ! ! !。兩個月以前找工做,好多面試官都問 spring MVC 相關的東西。例如:若是要解析多種視圖應該如何配置? springMVC中的事物怎麼配置?還有其餘的一些相關問題,而後我都說不會,是架構師已經配置好的。好尷尬啊。javascript
那就從零開始學起吧。css
首先下載所須要的 jar 包,並放入 lib 中。html
jar 包連接:http://pan.baidu.com/s/1qYQ0U3ijava
而後在 web.xml中進行配置。程序員
<servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置Spring mvc下的配置文件的位置和名稱 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring_mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
接着就要配置 spring_mvc.xml 這個 配置文件了。web
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置自動掃描的包 掃描 該包下面全部 有 @Controller 註解的類--> <context:component-scan base-package="com.ding.controller"/> <!-- 配置視圖解析器 如何把handler 方法返回值解析爲實際的物理視圖 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"></property> <property name="suffix" value=".jsp"></property> </bean> <mvc:annotation-driven/> </beans>
先配置須要掃描的 controller包,再配置 返回的視圖解析器。面試
我配置的試圖解析器的含義:若是 一個方法 最終返回是這樣 : return "result" 。那麼試圖解析器就回去找/Web-INF/view/result.jsp,找到就順利解析,找不到就404.如今通常視圖都放在 Web-INF下面,由於這個包是受保護的,經過鏈接沒法直接對裏面的文件進行訪問。ajax
接着就是建立 controller 了。因爲我上面配置的時候,須要掃面的 controller包 的全稱是 com.ding.controller,所以須要在這個包下面進行建立。spring
類上面的 @RequestMapping("") 能夠不寫,若是不寫,那就直接訪問方法上的 路徑,可是通常狀況下都會寫,由於項目裏要分不少模塊,每一個模塊都有本身的命名,寫了以後更容易區分和管理。spring-mvc
這裏我先寫了個BaseController,等下詳細介紹。
如今啓動項目,並進行訪問:http://localhost:8080/base_project/ding/test.do
如今已經成功了。最簡單的 springMVC項目已經搭建成功。開始介紹 個人 BaseController .
在項目中, HttpServletRequest,HttpServletResponse,HttpSession 這3個東西是常常要用的到,難道每次都本身手寫?太累了吧。因此,咱們寫一個類,讓這個類來建立,子類繼承這個類,直接使用這3個變量就能夠。
package com.ding.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; @Controller public class BaseController { protected HttpServletRequest request; protected HttpServletResponse response; protected HttpSession session; @ModelAttribute public void exc(HttpServletRequest request,HttpServletResponse response){ this.request=request; this.response=response; this.session=request.getSession(); } }
這樣子以後,每次一進方法,就能夠直接使用 request ,response ,session。簡便不少。
@ModelAttribute的含義:被@ModelAttribute註釋的方法會在此controller每一個方法執行前被執行,也就是說,你在調用本身須要的方法前,他就給你初始化好 request ,response ,session。
慢慢再深刻,如今我要是訪問靜態資源,例如 js,css這些辦?據我所知有兩種,第一種:
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.gif</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping>
在 web.xml中添加以上配置,就能夠了。但我感受仍是不太好,因此我喜歡第二種:
在 spring_mvc.xml 中多加一個配置:
<!--對靜態資源文件的訪問--> <mvc:resources mapping="/statics/**" location="/statics/" />
這樣就能夠了。statics包專門用於存放靜態資源。
上面說到的,若是須要解析多種視圖,那該怎麼辦?
這就要用到 order 屬性了。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"></property> <property name="suffix" value=".jsp"></property> <property name="order" value="10"></property> </bean>
order值 越大,越排在後面。就是小的先來,大的排隊。
很重要的一點:InternalResourceViewResolver 必須優先級最低,這固然是有緣由的:
當處理器返回邏輯視圖時(也就是return 「string」),要通過視圖解析器鏈,前面的解析器能處理的,就不會繼續往下傳播。
若是不能處理就要沿着解析器鏈繼續尋找,直到找到合適的視圖解析器(歸納爲:能解析的,不繼續往下找,不能解析的,要繼續往下找解析器)。
當處理器(@controller)返回的邏輯視圖解析過程:
當通過視圖解析器1時,若是能解析就解析並且不會再繼續往下。若是不能執行就返回null,這樣下面的解析器才能處理。
可是對於解析器InternalResourceViewResolver來講,無論能不能解析它都不會返回null,也就是說它攔截了全部的邏輯視圖,
讓後續的解析器得不到執行,因此InternalResourceViewResolver必須放在最後。
記住一點:不能解析就返回null,這樣後續解析器才能解析。
接着就是 spring MVC 的自定義攔截器了。它能夠作不少東西,最簡單的最多見的就是 登錄攔截。
要想實現自定義攔截,須要 咱們寫的類 實現 HandlerInterceptor 接口 或者 繼承 HandlerInterceptorAdapter 類。
先說 實現 HandlerInterceptor 接口的寫法:
package com.ding.intercepter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class LoginInterceptor implements HandlerInterceptor{ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3)throws Exception { } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3)throws Exception { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaa"); return true; } }
實現接口以後,分別須要實現3個方法 分別是 perHandle , afterCompletion , postHandle
preHandle():這個方法在業務處理器處理請求以前被調用,在該方法中對用戶請求request進行處理。若是程序員決定該攔截器對請求進行攔截處理後還要調用其餘的攔截器,或者是業務處理器去進行處理,則返回true;若是程序員決定不須要再調用其餘的組件去處理請求,則返回false。
postHandle():這個方法在業務處理器處理完請求後,可是DispatcherServlet向客戶端返回請求前被調用,在該方法中對用戶請求request進行處理。
afterCompletion():這個方法在DispatcherServlet徹底處理完請求後被調用,能夠在該方法中進行一些資源清理的操做。
通常狀況下,咱們都只用 preHandle 方法,其餘兩個方法不多使用。return false 表示,請求被中斷,不會繼續去訪問 controller了。
下面給出一個簡化版的登陸攔截:
package com.ding.intercepter; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class LoginInterceptor implements HandlerInterceptor{ private static String AJAX_TIME_OUT = null; static{ AJAX_TIME_OUT = "ajaxUserTimeout"; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3)throws Exception { } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3)throws Exception { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { String path = request.getServletPath(); if(path.indexOf(".shtml")<0){// test/ding 不須要登陸操做,因此不攔截 return true; }else{//test/login.shtml 須要登陸操做,因此攔截 HttpSession session=request.getSession(); String user=(String) session.getAttribute("user"); if(user==null || "".equals(user)){ String type = request.getHeader("X-Requested-With"); if(type!=null && ! "".equals(type) && type.equalsIgnoreCase("XMLHttpRequest")){//ajax請求,在頁面輸出錯誤信息 PrintWriter printWriter = response.getWriter(); printWriter.print(AJAX_TIME_OUT); printWriter.flush(); printWriter.close(); }else{ response.sendRedirect("");//跳轉到 登陸那裏 } return false; }else{ return true; } } } }
在 jqery 中添加以下代碼,捕獲 ajaxUserTimeout:
$(document).on('ajaxError',function(event, request, options){ var data = request.responseText; if(data == 'ajaxUserTimeout'){ window.top.location.href= ''; } });
這樣就攔截成功了。固然,還須要在 spring_mvc.xml中進行配置:
<mvc:interceptors> <mvc:interceptor> <!-- /** 表示全部的url,包括子url路徑 --> <mvc:mapping path="/**"/> <bean class="com.ding.intercepter.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
接着說說 繼承 HandlerInterceptorAdapter 類的寫法。其實差很少,可是當我繼承以後,沒有錯誤提示,要我本身去寫
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { }
感受麻煩了點。裏面的內容到是差不錯的。
那麼新的問題就來了,若是某些請求我不要攔截器攔截呢?應該怎麼辦?
如今的要求是不通過攔截器了,那就要有另外的配置。
<mvc:exclude-mapping path="/ding/**"/>,他放在 <mvc:mapping path="/**" /> 就能夠。
<mvc:interceptors> <mvc:interceptor> <!-- /** 表示全部的url,包括子url路徑 --> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/test/**"/> <bean class="com.ding.intercepter.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
如今, http://localhost:8080/base_project/test/xiao 這個路徑就不通過登陸攔截器了,直接訪問 controller。
在項目中咱們常常要用到文件上傳,springMVC本身就已經作好了,咱們配置一下就能夠用。
要使用這個文件上傳功能,須要先引入2個 jar 包。
commons-fileupload-1.3.1.jar
commons-io-2.4.jar
這兩個已經在上面 jar 的壓縮包裏面了,無需重複下載。
我本身在用的時候,一開始沒有下載並放進去,把這段配置放進去,一請求就報錯。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8" /> <!-- 編碼類型 --> <property name="maxUploadSize" value="1048576000000" /> <!-- 上傳的文件的最大size --> <property name="maxInMemorySize" value="40960" /> <!-- 在內存中所佔內存的最大值 --> </bean>
在 java程序中:
//單文件上傳 @RequestMapping("fileUpload") public String fileUpload(@RequestParam("file") CommonsMultipartFile file) throws IOException { File newFile=new File("D:/a.txt");//目標文件 file.transferTo(newFile);//經過CommonsMultipartFile的方法直接寫文件(注意這個時候) return ""; }
//多文件上傳 @RequestMapping("fileUpload") public String fileUpload(){ MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; Map<String, MultipartFile> files = multiRequest.getFileMap(); Iterator<Entry<String, MultipartFile>> iter = files.entrySet().iterator(); try { while (iter.hasNext()) { Map.Entry<java.lang.String, MultipartFile> entry = (Map.Entry<String, MultipartFile>) iter.next(); String name = entry.getValue().getOriginalFilename(); File writeFile = new File(""); CommonsMultipartFile file = (CommonsMultipartFile) files.get(entry.getKey()); if(!file.isEmpty()){ file.getFileItem().write(writeFile); } } } catch (Exception e) { e.printStackTrace(); } return ""; }
總的來講就兩種方式:
file.getFileItem().write(writeFile); file.transferTo(writeFile);
具體哪一種上傳效率高,還真沒試過。不過用的一直是 .wtite()方法
我在上傳的時候,文件中的中文亂碼了,因此須要解決。
最簡單的:在web.xml中添加以下配置。
<filter> <filter-name>Encoding</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>Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
全部請求都用utf-8 編碼。
想了下,springMVC還有很多東西,例如: aop,事物的配置,springMVC與 EhCache的集成,restful,統一異常處理,quartz與 springMVC的集成,mybatis與springMVC的集成。等我先研究一下,弄懂怎麼配置以後再來寫。