轉(zhuǎn)眼間又到了三月花開的季節(jié),沒一起感覺開篇都在說一些廢話,這一期一樣不例外。
項目github地址:https://github.com/pc859107393/SpringMvcMybatis
實時項目同步的地址是國內(nèi)的碼云:https://git.oschina.net/859107393/mmBlog-ser
我的簡書首頁是:http://www.itdecent.cn/users/86b79c50cfb3/latest_articles
上一期是:[手把手教程][第二季]java 后端博客系統(tǒng)文章系統(tǒng)——No6

工具
- IDE為idea16
- JDK環(huán)境為1.8
- gradle構(gòu)建,版本:2.14.1
- Mysql版本為5.5.27
- Tomcat版本為7.0.52
- 流程圖繪制(xmind)
- 建模分析軟件PowerDesigner16.5
- 數(shù)據(jù)庫工具MySQLWorkBench,版本:6.3.7build
本期目標
- 回顧SpringMvc
- 總結(jié)提高
閑聊
最近看了很多技術(shù)相關(guān)的文檔感覺受益良多,我也會不定時的在群里分享一些技術(shù)文檔,雖然很多都是別人分享給我的。
世上無難事,只要肯攀登。(肯攀登是誰呢?)
雖然說我們是單獨做技術(shù)的人,但是牛奶和面包總是不能少的。一味畫餅充饑的創(chuàng)業(yè)都是耍流氓。
最近碰到幾個想找我一起創(chuàng)業(yè)的人,不簽合同,不給現(xiàn)金,都是吹逼項目牛逼,然后想我白干。 可惜我已經(jīng)過了幾句煽情的話就能打動的年紀。
無論誰找你創(chuàng)業(yè),不給真金白銀都是耍流氓。
SpringMvc
在前面我們已經(jīng)做過很多關(guān)于SpringMvc的應(yīng)用,可能大家很多日常的基本操作都有了概念,但是這個操作叫做什么名字呢?怎樣更加合理的運用呢?Let's go!
1. 配置SpringMvc
我們需要創(chuàng)建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相關(guān)的申明。緊接著,我們需要開始一步步的注冊web相關(guān)的資源:
<!-- 配置SpringMVC -->
<!-- 1.開啟SpringMVC注解模式 -->
<!-- 簡化配置:
(1)自動注冊DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter
(2)提供一些列:數(shù)據(jù)綁定,數(shù)字和日期的format @NumberFormat, @DateTimeFormat, xml,json默認讀寫支持
-->
<mvc:annotation-driven/>
當(dāng)我們引入了上面的代碼的時候,我們已經(jīng)開啟了SpringMvc的注解映射(同理我們可以使用非注解映射!)
- 簡單的說明下非注解映射就是在Spring-web.xml中直接配置url。
當(dāng)我們配置了url的訪問注解后,按照道理來說只要我們開始配置了,那么我們的地址就是可以開始訪問的,但是我們不可能所有的請求都需要框架來處理然后轉(zhuǎn)發(fā)吧?so,我們需要配置一些特殊資源的訪問路徑,比如說靜態(tài)的js、css、img等等,所以有了如下的配置:
<!--配置靜態(tài)資源的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/>
當(dāng)然,我們上面得只是簡單的配置了一些文件的url目錄,同時我們可以部署我們的線上資源的訪問權(quán)限,如果是非法用戶那么就需要把他趕出服務(wù)器,所以我們需要請求攔截,并插入一些代碼檢查之類的操作,所以先配置攔截器如下:
<!-- 訪問攔截 -->
<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查詢到的用戶信息
/*
我們攔截的網(wǎng)址是需要最少是作者權(quán)限才能進行編輯的,所以這里我們需要限制訪問。
<br/> 同時我們可以看到的是 登錄頁面必須是所有人都可以訪問的,但是如果已經(jīng)登錄成功了,session在有效期內(nèi),我們的登錄界面就不應(yīng)該再展示給用戶
*/
if (!path.matches(".*/((endSupport)|(commit*)).*")) {
if (path.contains("/main/login")) { //已經(jīng)登錄且身份信息且沒有過期,我們直接跳轉(zhuǎn)到后端主頁去
try {
userId = request.getSession().getAttribute("userId").toString();
user = userService.findOneById(userId);
if (request.getRequestedSessionId().equals(user.getUserSessionId())) { //前面用戶登錄后會存入請求的sessionId和當(dāng)前的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和當(dāng)前的sessionId對比
throw new Exception("用戶信息錯誤!");
}
return true;
} catch (Exception e) {
e.printStackTrace();
response.sendRedirect(request.getContextPath() + "/main/login");
return false;
}
}
}
}
看到上面攔截器相關(guān)的代碼,大家都會明白,在我的url中只要匹配了endSupport和commit相關(guān)字段的,我們都需要用戶登錄,其實說嚴格一點按照http請求的幾種方式和restful來說沒我們需要匹配更多的規(guī)則才行。重點思考:我們既然前面看到了攔截器那里僅僅配置了一個攔截器,那么我們能不能配置多個攔截器呢?答案是可以的,interceptors:指定攔截器鏈,攔截器的執(zhí)行順序就是此處添加攔截器的順序。
通過上面的配置,我們已經(jīng)可以實現(xiàn)html頁面的展示,而且依賴js腳本刷新的動態(tài)頁面也是基本可以顯示了,但是,不是所有的后端工程師都精通web頁面的開發(fā),更多時候,可能我們單純的更喜歡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>
配置完成到這里,我們需要的就只剩下文件上傳的數(shù)據(jù)模型和web接口的掃描了。為什么要掃描web接口呢?我們前面開啟了注解映射,那么我們避免和項目其他的沖突,我們直接指定web接口的文件存放目錄,讓程序完成自動掃描,那么我們就只需要關(guān)心我們的業(yè)務(wù)開發(fā)了,是不是很爽?
<!--上傳文件的處理模型-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10000000"/>
</bean>
<!-- 4.掃描web相關(guān)的bean -->
<context:component-scan base-package="cn.acheng1314.controller">
<!-- 制定掃包規(guī)則 ,只掃描使用@Controller注解的JAVA類 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
小結(jié)
SpringMvc的配置順序如下:
創(chuàng)建配置文件→加入依賴申明→開啟注解式映射(非注解也行)→加入特殊資源訪問映射→設(shè)置訪問攔截→配置動態(tài)視圖解析→配置web接口掃描規(guī)則和文件上傳的處理模型。
既然已經(jīng)配置好了注解開發(fā)設(shè)置,那么我們需要實際操作體會一下SpringMvc的開發(fā),先看看代碼如下:
@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。主要規(guī)則說明如下:
類用Controller的注解:@Controller,說明這個類是web接口的注冊類
RequestMapping的意思是說明這個地址是:“/RequestMapping”
-
http請求的方式有很多,可以在RequestMapping中限制具體的請求方式是什么?
- 具體的形式有:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
- 我們在RequestMapping中發(fā)現(xiàn)有更多的說明,如:name、value、path、method、params、headers、consumes、produces,具體的其他用途我們可以詳細的查看文檔。
ModelAndView主要是用來說明這個請求地址是個web視圖。
view.addObject() 是用來將我們希望傳輸?shù)絯eb頁面的數(shù)據(jù)插入到請求中
在web頁面中,我們使用${objName}來獲取數(shù)據(jù)。
通過上面得代碼和歸納,我們大概明白了怎么去創(chuàng)建一個web頁面需要怎么樣的設(shè)置,但是我們?nèi)绻@取到j(luò)son數(shù)據(jù)呢?具體如下:
@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數(shù)據(jù)";
}
上面得代碼我們可以得到j(luò)son數(shù)據(jù),為什么呢?
- 首先我們可以看到我們的RequestMapping多了produces和method。produces是對這個方法的綜述,method是這個請求的訪問方法,重要的是他們都是數(shù)組,也就是可以支持多種形式。
- ResponseBody這個注解是專門用來修飾某個方法,說明這個方法只返回ResponseBody(響應(yīng)體)。
- RequestParam標記這個請求的參數(shù),默認值是TRUE,除非單獨表明required = false
這就完了嗎?等等呢,還沒完。為啥呢?我們想象一下,當(dāng)我們要去找人的時候,是不是直接叫xx出來呢?既然這樣,我現(xiàn)在需要從一個列表中拿到數(shù)據(jù),是不是也應(yīng)該這樣呢?
/**
* RESTful風(fēng)格的文章頁面
*
* @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發(fā)生了變化,其他的都是對應(yīng)著變化的。
- 首先在方法上面注解RequestMapping說明請求地址。
- path = "/post/{postId}" 說明url的形式是:/post/xxx。
- @PathVariable int postId說明這里參數(shù)postId和上面的{postId}的值相同。
說實話,到了上面這樣子,其實大概東西都差不多了,不過需要注意的重點就是:web接口中的參數(shù)永遠你都是不知道存不存在的,所以使用基本數(shù)據(jù)類型的參數(shù)都是不安全的,所以我們需要使用包裝類型。
總結(jié)
Spring+SpringMvc類型的框架中,SpringMvc提供了web試圖的填充和解析以及http請求的接收、處理和響應(yīng),所以我們需要先配置web相關(guān)設(shè)置,后面才能進行web相關(guān)的開發(fā)。
最后扯犢子一下,關(guān)于中國人、樂天、薩德和韓國我覺得尊重中國人,保護私有財產(chǎn),韓國棒子滾一邊去。