轉眼間又到了三月花開的季節,沒一起感覺開篇都在說一些廢話,這一期一樣不例外。
專案github地址:github.com/pc859107393…
實時專案同步的地址是國內的碼雲:git.oschina.net/859107393/m…
我的簡書首頁是:www.jianshu.com/users/86b79…
上一期是:[手把手教程][第二季]java 後端部落格系統文章系統——No6
工具
- IDE為idea16
- JDK環境為1.8
- gradle構建,版本:2.14.1
- Mysql版本為5.5.27
- Tomcat版本為7.0.52
- 流程圖繪製(xmind)
- 建模分析軟體PowerDesigner16.5
- 資料庫工具MySQLWorkBench,版本:6.3.7build
本期目標
- 回顧SpringMvc
- 總結提高
閒聊
最近看了很多技術相關的文件感覺受益良多,我也會不定時的在群裡分享一些技術文件,雖然很多都是別人分享給我的。
世上無難事,只要肯攀登。(肯攀登是誰呢?)
雖然說我們是單獨做技術的人,但是牛奶和麵包總是不能少的。一味畫餅充飢的創業都是耍流氓。
最近碰到幾個想找我一起創業的人,不籤合同,不給現金,都是吹逼專案牛逼,然後想我白乾。 可惜我已經過了幾句煽情的話就能打動的年紀。
無論誰找你創業,不給真金白銀都是耍流氓。
SpringMvc
在前面我們已經做過很多關於SpringMvc的應用,可能大家很多日常的基本操作都有了概念,但是這個操作叫做什麼名字呢?怎樣更加合理的運用呢?Let's go!
1. 配置SpringMvc
我們需要建立Spring-Web.xml,代表它是我們的Spring依賴的註冊檔案(context)。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">複製程式碼
上面演示的是我們註冊的時候需要的Spring相關的申明。緊接著,我們需要開始一步步的註冊web相關的資源:
<!-- 配置SpringMVC -->
<!-- 1.開啟SpringMVC註解模式 -->
<!-- 簡化配置:
(1)自動註冊DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter
(2)提供一些列:資料繫結,數字和日期的format @NumberFormat, @DateTimeFormat, xml,json預設讀寫支援
-->
<mvc:annotation-driven/>複製程式碼
當我們引入了上面的程式碼的時候,我們已經開啟了SpringMvc的註解對映(同理我們可以使用非註解對映!)
- 簡單的說明下非註解對映就是在Spring-web.xml中直接配置url。
當我們配置了url的訪問註解後,按照道理來說只要我們開始配置了,那麼我們的地址就是可以開始訪問的,但是我們不可能所有的請求都需要框架來處理然後轉發吧?so,我們需要配置一些特殊資源的訪問路徑,比如說靜態的js、css、img等等,所以有了如下的配置:
<!--配置靜態資源的url對映-->
<mvc:resources mapping="/css/**" location="/static/css/"/>
<mvc:resources mapping="/images/**" location="/static/images/"/>
<mvc:resources mapping="/view/**" location="/static/view/"/>
<mvc:resources mapping="/js/**" location="/static/js/"/>
<mvc:resources mapping="/fonts/**" location="/static/fonts/"/>
<!--配置預設的前端控制器-->
<mvc:default-servlet-handler/>複製程式碼
當然,我們上面得只是簡單的配置了一些檔案的url目錄,同時我們可以部署我們的線上資源的訪問許可權,如果是非法使用者那麼就需要把他趕出伺服器,所以我們需要請求攔截,並插入一些程式碼檢查之類的操作,所以先配置攔截器如下:
<!-- 訪問攔截 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**/**"/>
<bean class="cn.acheng1314.intercepter.LoginHandlerInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>複製程式碼
具體的攔截器程式碼如下:
/**
* Created by mac on 2017/1/27.
*/
public class LoginHandlerInterceptor extends HandlerInterceptorAdapter {
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String path = request.getServletPath();
String userId; //登入成功後寫入session的使用者id
User user; //通過使用者ID查詢到的使用者資訊
/*
我們攔截的網址是需要最少是作者許可權才能進行編輯的,所以這裡我們需要限制訪問。
<br/> 同時我們可以看到的是 登入頁面必須是所有人都可以訪問的,但是如果已經登入成功了,session在有效期內,我們的登入介面就不應該再展示給使用者
*/
if (!path.matches(".*/((endSupport)|(commit*)).*")) {
if (path.contains("/main/login")) { //已經登入且身份資訊且沒有過期,我們直接跳轉到後端主頁去
try {
userId = request.getSession().getAttribute("userId").toString();
user = userService.findOneById(userId);
if (request.getRequestedSessionId().equals(user.getUserSessionId())) { //前面使用者登入後會存入請求的sessionId和當前的sessionId對比
response.sendRedirect(request.getContextPath() + "/endSupport/index");
return false;
}
} catch (Exception e) {
e.printStackTrace();
return true;
}
}
return true;
} else {
try {
userId = request.getSession().getAttribute("userId").toString();
user = userService.findOneById(userId);
if (!request.getRequestedSessionId().equals(user.getUserSessionId())) { //前面使用者登入後會存入請求的sessionId和當前的sessionId對比
throw new Exception("使用者資訊錯誤!");
}
return true;
} catch (Exception e) {
e.printStackTrace();
response.sendRedirect(request.getContextPath() + "/main/login");
return false;
}
}
}
}複製程式碼
看到上面攔截器相關的程式碼,大家都會明白,在我的url中只要匹配了endSupport和commit相關欄位的,我們都需要使用者登入,其實說嚴格一點按照http請求的幾種方式和restful來說沒我們需要匹配更多的規則才行。重點思考:我們既然前面看到了攔截器那裡僅僅配置了一個攔截器,那麼我們能不能配置多個攔截器呢?答案是可以的,interceptors:指定攔截器鏈,攔截器的執行順序就是此處新增攔截器的順序。
通過上面的配置,我們已經可以實現html頁面的展示,而且依賴js指令碼重新整理的動態頁面也是基本可以顯示了,但是,不是所有的後端工程師都精通web頁面的開發,更多時候,可能我們單純的更喜歡jsp這種方式的頁面,所以我們需要新增jsp頁面的解析,程式碼如下:
<!-- 3.配置jsp 顯示ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>複製程式碼
配置完成到這裡,我們需要的就只剩下檔案上傳的資料模型和web介面的掃描了。為什麼要掃描web介面呢?我們前面開啟了註解對映,那麼我們避免和專案其他的衝突,我們直接指定web介面的檔案存放目錄,讓程式完成自動掃描,那麼我們就只需要關心我們的業務開發了,是不是很爽?
<!--上傳檔案的處理模型-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10000000"/>
</bean>
<!-- 4.掃描web相關的bean -->
<context:component-scan base-package="cn.acheng1314.controller">
<!-- 制定掃包規則 ,只掃描使用@Controller註解的JAVA類 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>複製程式碼
小結
SpringMvc的配置順序如下:
建立配置檔案→加入依賴申明→開啟註解式對映(非註解也行)→加入特殊資源訪問對映→設定訪問攔截→配置動態檢視解析→配置web介面掃描規則和檔案上傳的處理模型。
既然已經配置好了註解開發設定,那麼我們需要實際操作體會一下SpringMvc的開發,先看看程式碼如下:
@Controller
@RequestMapping("/front")
public class FrontWebController {
/**
* 返回主頁面
*
* @return
*/
@RequestMapping(value = "/main", method = RequestMethod.GET)
public ModelAndView frontMain(HttpServletRequest request) throws Exception {
ModelAndView view = new ModelAndView("frontMain");
view.addObject("framJson", getFramJson());
view.addObject("postsJson", findPublishPost(request, 1, 10));
return view;
}複製程式碼
在上面得程式碼中我們是返回一個web頁面,這個web頁面匹配到的url就是:xxxhost.xxx/front/main。主要規則說明如下:
- 類用Controller的註解:@Controller,說明這個類是web介面的註冊類
- RequestMapping的意思是說明這個地址是:“/RequestMapping”
http請求的方式有很多,可以在RequestMapping中限制具體的請求方式是什麼?
- 具體的形式有:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
- 我們在RequestMapping中發現有更多的說明,如:name、value、path、method、params、headers、consumes、produces,具體的其他用途我們可以詳細的檢視文件。
- ModelAndView主要是用來說明這個請求地址是個web檢視。
- view.addObject() 是用來將我們希望傳輸到web頁面的資料插入到請求中
- 在web頁面中,我們使用${objName}來獲取資料。
通過上面得程式碼和歸納,我們大概明白了怎麼去建立一個web頁面需要怎麼樣的設定,但是我們如果要獲取到json資料呢?具體如下:
@RequestMapping(value = "/findPublishPost"
, produces = {APPLICATION_JSON_UTF8_VALUE}
, method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public Object findPublishPost(HttpServletRequest request,
@RequestParam(value = "pageNum", required = false)
Integer pageNum,
@RequestParam(value = "pageSize", required = false)
Integer pageSize) throws Exception {
return "這是返回的json資料";
}複製程式碼
上面得程式碼我們可以得到json資料,為什麼呢?
- 首先我們可以看到我們的RequestMapping多了produces和method。produces是對這個方法的綜述,method是這個請求的訪問方法,重要的是他們都是陣列,也就是可以支援多種形式。
- ResponseBody這個註解是專門用來修飾某個方法,說明這個方法只返回ResponseBody(響應體)。
- RequestParam標記這個請求的引數,預設值是TRUE,除非單獨表明required = false
這就完了嗎?等等呢,還沒完。為啥呢?我們想象一下,當我們要去找人的時候,是不是直接叫xx出來呢?既然這樣,我現在需要從一個列表中拿到資料,是不是也應該這樣呢?
/**
* RESTful風格的文章頁面
*
* @param postId 文章ID
* @return 返回文章頁面
*/
@RequestMapping(path = "/post/{postId}", method = RequestMethod.GET)
public ModelAndView getPostView(@PathVariable int postId) {
ModelAndView resultView = new ModelAndView("frontPost");
resultView.addObject("framJson", getFramJson());
resultView.addObject("postJson", getPostById(postId));
return resultView;
}複製程式碼
上線這段程式碼,我們最終可以看到一個文章詳情頁,而且重點的是“post/”後面的postId發生了變化,其他的都是對應著變化的。
- 首先在方法上面註解RequestMapping說明請求地址。
- path = "/post/{postId}" 說明url的形式是:/post/xxx。
- @PathVariable int postId說明這裡引數postId和上面的{postId}的值相同。
說實話,到了上面這樣子,其實大概東西都差不多了,不過需要注意的重點就是:web介面中的引數永遠你都是不知道存不存在的,所以使用基本資料型別的引數都是不安全的,所以我們需要使用包裝型別。
總結
Spring+SpringMvc型別的框架中,SpringMvc提供了web試圖的填充和解析以及http請求的接收、處理和響應,所以我們需要先配置web相關設定,後面才能進行web相關的開發。
最後扯犢子一下,關於中國人、樂天、薩德和韓國我覺得尊重中國人,保護私有財產,韓國棒子滾一邊去。