【黑馬旅遊網】項目完結+未完成功能實現+個人總結+bug記錄


算是經歷了整整四天吧,前兩天聽課跟着視頻敲。後兩天自己手動完成剩餘的其他若干功能,一路debug過來,收穫許多,在此記錄。希望自己永遠保持熱忱,加油。

對了,代碼上傳到碼雲:https://gitee.com/tqbx/travel-maven-web.git
歡迎交流,菜雞求教~(ps:github好慢好慢)

一、項目配置問題

1、【maven項目目錄結構】

可能一開始創建出來的項目文件目錄形式不對,IDEA對目錄結構有明顯的要求,可以通過以下方法修改,當然其他情況也可以使用;

在這裏插入圖片描述

2、【修改目錄屬性】

在這裏插入圖片描述

後來發現可以直接右鍵點擊目錄,選擇make directory as 效果是一樣的。

在這裏插入圖片描述

3、【設置web源目錄】

在這裏插入圖片描述

4、【maven低版本和servlet3.0衝突】

視頻中使用的是Servlet3.0之後的版本,利用註解配置,我原來之前一直也是註解配置,想試着使用xml配置練練手。但是當我從xml到註解轉換的過程中,遇到了一些問題,主要是Servlet版本和maven倉庫版本衝突?具體我也不太清清除,過於真實,整個過程迷迷糊糊的,bug頻出,也試過很多方法,真的不知道是哪個方法起了作用。

以下內容僅記錄自己的糾錯過程,首先我先去視頻中的代碼文件查看一下有偏差的地方:直接鎖定是Servlet的版本問題,servlet3.0之後纔可以使用註解,而我使用的是2.5。

接着,我參考了這個博客:Maven創建webapp骨架無法使用@WebServlet來實現註解配置解決方案,修改了maven倉庫中的jar包的web.xml內容,可能是我操作的問題,並沒有見效。

接着又在某個論壇上看到一個方法,可以重新指定xml的版本。【後面測試了幾次,貌似和這個關係不大】

在這裏插入圖片描述
在這裏插入圖片描述

接着在pom.xml中添加servlet3.0之後的依賴,需要注意的是,要指定scope爲provide,不然的話可能會產生衝突。【一定要注意找到填寫正確座標,有一次我把artifactId裏寫成servlet-api死活下載不來】

        <!--Servlet-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

就像這樣的衝突錯誤(下面兩種都是因爲scope沒有指定privided的原因,因爲添加上去就成功了)

com.travel.web.filter.CharacterFilter cannot be cast to javax.servlet.Filter

javax.servlet.ServletException: java.lang.LinkageError

中途會遇到缺少javaEE啥啥啥的,annotation包依賴缺少的錯誤,按照提示添加即可。

以上就是我從web.xml到註解配置的全過程,有點坎坷,但是今後遇到這樣的問題,興許會多一些思路吧。

5、【控制檯輸出亂碼解決】

在這裏插入圖片描述

settings->Build,Execution,Deployment->Build Tools->Maven->Runner

設置VM-Option參數,指定虛擬機字符集:-Dfile.encoding=gb2312,如果不行可以設置稱其他的。

二、前臺代碼

1、【發送異步請求】

//校驗通過,ajax發送請求,提交表單數據  $("#registerForm").serialize()
$.post("registerUserServlet",$(this).serialize(),function (data) {

    if(data.flag){
        //註冊成功,跳轉成功頁面
        location.href= "register_ok.html";
    }else{
        //如果錯誤,需要重新對驗證碼servlet請求一次,不然會導致會話中的驗證碼消失,圖片雖然存在,但碼已經沒有了
		document.getElementById("check_img").src= "checkCode?"+new Date().getTime();
        //註冊失敗,給errormsg添加提示信息
        $("#error_msg").html(data.errorMsg);
    }
})

2、【校驗手機號格式】

var reg_telephone = /^1(3|4|5|7|8)\d{9}$/;

3、【校驗郵箱格式】

var reg_email = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;

4、【失去焦點事件】

注意此時將函數名作爲Function對象傳入,沒有()。

$("#username").blur(checkUsername);

5、【前後端交互】

將信息封裝爲對象,是值得我學習的地方。

public class ResultInfo implements Serializable {
    private boolean flag;//後端返回結果正常爲true,發生異常返回false
    private Object data;//後端返回結果數據對象
    private String errorMsg;//發生異常的錯誤消息
}

6、【html的onclick()事件】

參考:https://blog.csdn.net/ywl570717586/article/details/53130863,該博客分割線以下內容。

7、【jackson】

利用mapper對象操作json數據

import com.fasterxml.jackson.databind.ObjectMapper;

//將info對象序列化爲json,返回客戶端
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
//將json數據寫回客戶端
//設置content-type
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);

8、【checkbox】

沒有指定value屬性得時候,傳遞過去的值爲on!!!

9、【獲取url中拼接的參數】

//根據傳遞過來的參數name獲取對應的值
function getParameter(name) {
    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)","i");
    var r = location.search.substr(1).match(reg);
    if (r!=null) return (r[2]); return null;
}

三、工具類的使用

利用【MailUtils】完成郵箱發送,不過得在郵箱設置裏面申請開啓服務。

利用【uuid工具類】完成隨機激活碼的生成。

利用【JedisUtils】完成redis客戶端的獲取,從而操作redis數據庫。

利用【JDBCUtils】封裝druid連接池,返回數據源對象。

四、路徑分發思想

參考HttpServlet的service方法對請求的方式進行路徑分發,對應不同的方法,完成不同的類似Servlet完成的功能,真的受益匪淺,回過頭來思考原本需要定義那麼多那麼多的Servlet,現在完全封裝到一個UserServlet中,妙啊。

【分發Servlet】

try {
    Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
    //調用方法
    method.invoke(this,req,resp);

} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
    e.printStackTrace();
}

報錯:java.lang.NoSuchMethodException

原因:調用方法是Protected修飾的。

解決:

  1. 忽略訪問權限修飾符+暴力破解。

    try {
        //忽略訪問權限修飾符
        Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
        //暴力破解
        method.setAccessible(true);
        //調用方法
        method.invoke(this,req,resp);
    
    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
        e.printStackTrace();
    }
    
  2. 直接將調用的方法權限修改爲public即可。(看到這操作,忍不住笑了)

五、數據庫問題

1、【連接問題】

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

報錯原因:可能是因爲MySQL服務沒有開啓,打開services.msc,開啓MySQL就ok了。

類似的問題還有redis服務端未開啓:redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out

2、【mysql語句】

報錯:Every derived table must have its own alias

SELECT COUNT(*) FROM ( select * from department) AS aa; -- 語法如此,需要給子查詢的表加上別名。

3、【緩存優化】

有些資源每次加載頁面都會重新請求數據庫數據來加載,對數據庫的壓力比較大,且這些數據不會經常發生變化,可以進行緩存優化。

  1. 在service層中,首先判斷數據是否存在於redis緩存中,如果有的話直接從緩存中獲取。
  2. 如果緩存中沒有,例如第一次請求時緩存中還不存在,這就需要去數據庫中查詢,並將查詢得到的數據添加進緩存。

https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/105167978

五、個人對項目一些細微不足的優化

1、【Alibaba Java Coding Guidelines】

這個插件下載之後,才發現自己原來寫的註釋那麼沒有原則,哈哈,這個東西對於有強迫症的人來說,簡直魔鬼無疑。

2、【抽取了驗證碼校驗功能】

    private boolean checkCode(HttpServletRequest request,HttpServletResponse response) throws IOException {
        //驗證碼校驗
        String check = request.getParameter("check");

        HttpSession session = request.getSession();
        String checkCodeServer = (String) session.getAttribute("checkCode");

        //保證驗證碼只能使用一次
        session.removeAttribute("checkCode");
        //驗證碼不相等
        if(checkCodeServer == null||!checkCodeServer.equalsIgnoreCase(check)){
			//對用戶輸入驗證碼進行判斷
            if("".equals(check)){
                info.setErrorMsg("驗證碼不能爲空");
            }else {
                info.setErrorMsg("驗證碼錯誤");
            }
            info.setFlag(false);
            response.setContentType(JSON_CONTENT_TYPE);
            String s = mapper.writeValueAsString(info);
            response.getWriter().write(s);
            return false;
        }
        return true;
    }

3、【關於list==null和list.size()==0】

這也是我在回頭看代碼的時候思考的一個問題,曾經在一些微信公衆號上見過類似的科普。

有時候dao層可能會產生list集合爲空的情況,比如沒有查詢到list,這時候如果返回null,在service層就需要做相應的非null判斷,有時候可能會忘記。我最初的想法是初始化一個空的ArrayList,List<Category> list = new ArrayList();

![優化](E:\1JavaBlog\maven\pic\優化.png)![優化](E:\1JavaBlog\maven\pic\優化.png)    @Override
    public List<Category> findAll() {
        //List<Category> list = Collections.emptyList();
        List<Category> list = new ArrayList<>();
        try{
            String sql = "select * from tab_category";
            list = template.query(sql,new BeanPropertyRowMapper<>(Category.class));
        }catch (Exception e){
        }
        return list;
    }

在查找資料的過程中,發現Collections集合類有專門產生空集合的方法,例如List<Category> list = Collections.emptyList();,查看他的源碼可以發現,實際上它創建一個靜態內部類的對象private static class EmptyList<E>。更特別的是,產生的list並沒有我們熟悉的add,remove等方法,對他進行這些操作會直接拋出UnsupportedOperationException異常。

https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/105168546

六、實現未完成的功能

1、【加載完成時,讓大圖成爲第一張】

在這裏插入圖片描述

2、【增加驗證碼爲空的信息,並且刷新驗證碼】

避免圖文不符。

在這裏插入圖片描述

3、【增加我的收藏及分頁功能】

<a href="javascript:void(judgeUser())" id="myFavorite" class="collection">我的收藏</a>

//點擊我的收藏
judgeUser = function (){
    //未登錄
    if(user == null){
        alert("您尚未登錄,請登錄!")
        location.href = "http://localhost/travel/login.html";
    }else{
        //已登錄a
        // alert(user.uid);
        var uid = user.uid;
        // http://localhost/travel/route/pageFavorite?uid=7
        location.href = "http://localhost/travel/myfavorite.html?uid=7";
    }
}

在這裏插入圖片描述

在這裏插入圖片描述

4、【增加了自動登錄功能】

利用cookie技術,在客戶端存儲賬號密碼,實現自動登錄。

5、【增加了熱門推薦功能】

增加熱門推薦,並連接路線具體信息。

在這裏插入圖片描述

6、【增加了首頁三大種類旅遊路線的顯示功能】

在這裏插入圖片描述

但是上面對應的樣式是真的不知道哪裏改,經過debug,發現點擊狀態下會激活active樣式是沒錯的,bug處在左邊三欄和右邊字體差了一格,希望知道怎麼修復的小夥伴教教我!不然也太難受了。

7、【增加了收藏排行榜展示分頁以及查詢功能】

在這裏插入圖片描述

在這裏插入圖片描述

這個部分在sql語句部分消耗了許多時間,表關係如下:

在這裏插入圖片描述

我的想法是,先根據tab_favorite中的rid進行分組,然後計算每個rid的數量,就是用戶收藏的個數,按照降序排列,生成新的子查詢表。然後在tab_route中尋找rid與子查詢表rid相同的路線,並進行where子查詢,模糊匹配路線名,以及金額大小,最後就可以獲得:

由收藏次數降序排序的route,並且是完全符合搜尋條件的。

    @Override
    public List<Route> findRouteByRangePage(int start, int pageSize, String rname, int first, int last) {
        //String sql = "select * from tab_route where cid = ? limit ? , ?";

        String sql = "SELECT * FROM (SELECT * FROM (SELECT rid,COUNT(rid) AS COUNT FROM tab_favorite " +
                "GROUP BY rid ORDER BY COUNT(rid) DESC)AS aa)AS bb,tab_route t WHERE t.rid = bb.rid ";
        StringBuilder sb = new StringBuilder();
        //條件們
        List params = new ArrayList();
        //判斷參數是否有值
        if(rname!=null&&rname.length()>0){
            sb.append("and t.rname like ?");
            params.add("%"+rname+"%");
        }
        if(first!=0){
            sb.append("and t.price > ? ");
            //添加?對應的值
            params.add(first);
        }
        if(last!=0){
            sb.append("and t.price < ? ");
            //添加?對應的值
            params.add(last);
        }
        //分頁
        sb.append("limit ? , ? ");
        sql += sb.toString();
        params.add(start);
        params.add(pageSize);
        return template.query(sql,new BeanPropertyRowMapper<>(Route.class),params.toArray());
    }

面前能夠完成需求,只是比較繁瑣,不知有沒有更好的方案,歡迎交流。

8、【一點小bug】

  1. js頁面字符串比較大小需要注意:console.log("5">"123");結果是true,因爲字符串比較回從前向後依次比較,如果希望數值比較,可以利用parseInt(first) > parseInt(last)
  2. 在查詢搜索的時候,由於沒有在本文框中輸入信息,因此數據返回的時候,頁碼部分的調用的ajax請求函數的參數將爲"",而不是null,調用favoriteRank(null, rname, first, last),會造成javascript:favoriteRank(2,,,)函數調用失敗。我想了個很蠢辦法:在調用之前對參數進行判斷,如果爲"",就認爲賦null。
if (rname === "" && first === "" && last === "") {
    favoriteRank(null, null, null, null)
} else if (rname === "" && first === "") {
    favoriteRank(null, null, null, last)
} else if (rname === "") {
    favoriteRank(null, null, first, last)
} else {
    favoriteRank(null, rname, first, last)
}

這樣子就可以解決了:

在這裏插入圖片描述

七、個人反思

  1. 總體來說在看視頻學習的時候能夠跟上思路,也許跟項目複雜度不高有些許關係。

  2. 在一些小知識掌握的不夠紮實,導致許多細節的地方四處尋找博客,時間消耗較多。

  3. 還有一個明顯的感受就是,聽的時候都會,自己做的時候就有點猶豫,生怕哪裏搞錯。

  4. 自己也對項目本身不足之處進行優化,例如驗證碼刷新失效、某些頁面跳轉、代碼部分重構等,這個過程還是挺鍛鍊我的排錯糾錯能力,debug漸漸熟練,原本的一些問題就越發容易解決。

  5. 最重要的一點:在敲代碼前一定要確定自己的思路,有了思路,寫起來真的很清晰!看老師從前臺分析到後臺,從servlet到service,再到dao,每一層的任務都劃分得清清楚楚,真的值得我學習再學習。

  6. 最近在培養這種列提綱的意識,真的吃邏輯,但列出來之後就感覺自己還是能行的,無非就是消耗的時間多一點。

  7. 其實之前看過一些spring,看到一些依賴注入的問題,其實理解沒有那麼深刻的,又回過頭來練練web項目,理解又更加深刻一些。

  8. 一起奮戰的兄弟們,加油!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章