[java手把手教程][第二季]java后端博客系統(tǒng)文章系統(tǒng)——No7

轉(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

行走的java全棧
行走的java全棧

工具

  • 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ī)則說明如下:

  1. 類用Controller的注解:@Controller,說明這個類是web接口的注冊類

  2. RequestMapping的意思是說明這個地址是:“/RequestMapping”

  3. http請求的方式有很多,可以在RequestMapping中限制具體的請求方式是什么?

    • 具體的形式有:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
    • 我們在RequestMapping中發(fā)現(xiàn)有更多的說明,如:name、value、path、method、params、headers、consumes、produces,具體的其他用途我們可以詳細的查看文檔。
  4. ModelAndView主要是用來說明這個請求地址是個web視圖。

  5. view.addObject() 是用來將我們希望傳輸?shù)絯eb頁面的數(shù)據(jù)插入到請求中

  6. 在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ù),為什么呢?

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

  1. 首先在方法上面注解RequestMapping說明請求地址。
  2. path = "/post/{postId}" 說明url的形式是:/post/xxx。
  3. @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),韓國棒子滾一邊去。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容