[java 手把手教程][第二季]java 後端部落格系統文章系統——No7

pc859107393發表於2017-03-14

轉眼間又到了三月花開的季節,沒一起感覺開篇都在說一些廢話,這一期一樣不例外。

專案github地址:github.com/pc859107393…

實時專案同步的地址是國內的碼雲:git.oschina.net/859107393/m…

我的簡書首頁是:www.jianshu.com/users/86b79…

上一期是:[手把手教程][第二季]java 後端部落格系統文章系統——No6

[java 手把手教程][第二季]java 後端部落格系統文章系統——No7
行走的java全棧

工具

  • 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。主要規則說明如下:

  1. 類用Controller的註解:@Controller,說明這個類是web介面的註冊類
  2. RequestMapping的意思是說明這個地址是:“/RequestMapping”
  3. http請求的方式有很多,可以在RequestMapping中限制具體的請求方式是什麼?

    • 具體的形式有:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
    • 我們在RequestMapping中發現有更多的說明,如:name、value、path、method、params、headers、consumes、produces,具體的其他用途我們可以詳細的檢視文件。
  4. ModelAndView主要是用來說明這個請求地址是個web檢視。
  5. view.addObject() 是用來將我們希望傳輸到web頁面的資料插入到請求中
  6. 在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資料,為什麼呢?

  1. 首先我們可以看到我們的RequestMapping多了produces和method。produces是對這個方法的綜述,method是這個請求的訪問方法,重要的是他們都是陣列,也就是可以支援多種形式。
  2. ResponseBody這個註解是專門用來修飾某個方法,說明這個方法只返回ResponseBody(響應體)。
  3. 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發生了變化,其他的都是對應著變化的。

  1. 首先在方法上面註解RequestMapping說明請求地址。
  2. path = "/post/{postId}" 說明url的形式是:/post/xxx。
  3. @PathVariable int postId說明這裡引數postId和上面的{postId}的值相同。

說實話,到了上面這樣子,其實大概東西都差不多了,不過需要注意的重點就是:web介面中的引數永遠你都是不知道存不存在的,所以使用基本資料型別的引數都是不安全的,所以我們需要使用包裝型別。

總結

Spring+SpringMvc型別的框架中,SpringMvc提供了web試圖的填充和解析以及http請求的接收、處理和響應,所以我們需要先配置web相關設定,後面才能進行web相關的開發。


最後扯犢子一下,關於中國人、樂天、薩德和韓國我覺得尊重中國人,保護私有財產,韓國棒子滾一邊去。

相關文章