文章目錄
- 一、項目配置問題
- 二、前臺代碼
- 1、【發送異步請求】
- 2、【校驗手機號格式】
- 3、【校驗郵箱格式】
- 4、【失去焦點事件】
- 5、【前後端交互】
- 6、【html的onclick()事件】
- 7、【jackson】
- 8、【checkbox】
- 9、【獲取url中拼接的參數】
- 三、工具類的使用
- 四、路徑分發思想
- 五、個人對項目一些細微不足的優化
- 六、實現未完成的功能
- 1、【加載完成時,讓大圖成爲第一張】
- 2、【增加驗證碼爲空的信息,並且刷新驗證碼】
- 3、【增加我的收藏及分頁功能】
- 4、【增加了自動登錄功能】
- 5、【增加了熱門推薦功能】
- 6、【增加了首頁三大種類旅遊路線的顯示功能】
- 7、【增加了收藏排行榜展示分頁以及查詢功能】
- 8、【一點小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修飾的。
解決:
-
忽略訪問權限修飾符+暴力破解。
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(); }
-
直接將調用的方法權限修改爲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、【緩存優化】
有些資源每次加載頁面都會重新請求數據庫數據來加載,對數據庫的壓力比較大,且這些數據不會經常發生變化,可以進行緩存優化。
- 在service層中,首先判斷數據是否存在於redis緩存中,如果有的話直接從緩存中獲取。
- 如果緩存中沒有,例如第一次請求時緩存中還不存在,這就需要去數據庫中查詢,並將查詢得到的數據添加進緩存。
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】
- js頁面字符串比較大小需要注意:
console.log("5">"123");
結果是true,因爲字符串比較回從前向後依次比較,如果希望數值比較,可以利用parseInt(first) > parseInt(last)
。 - 在查詢搜索的時候,由於沒有在本文框中輸入信息,因此數據返回的時候,頁碼部分的調用的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)
}
這樣子就可以解決了:
七、個人反思
-
總體來說在看視頻學習的時候能夠跟上思路,也許跟項目複雜度不高有些許關係。
-
在一些小知識掌握的不夠紮實,導致許多細節的地方四處尋找博客,時間消耗較多。
-
還有一個明顯的感受就是,聽的時候都會,自己做的時候就有點猶豫,生怕哪裏搞錯。
-
自己也對項目本身不足之處進行優化,例如驗證碼刷新失效、某些頁面跳轉、代碼部分重構等,這個過程還是挺鍛鍊我的排錯糾錯能力,debug漸漸熟練,原本的一些問題就越發容易解決。
-
最重要的一點:在敲代碼前一定要確定自己的思路,有了思路,寫起來真的很清晰!看老師從前臺分析到後臺,從servlet到service,再到dao,每一層的任務都劃分得清清楚楚,真的值得我學習再學習。
-
最近在培養這種列提綱的意識,真的吃邏輯,但列出來之後就感覺自己還是能行的,無非就是消耗的時間多一點。
-
其實之前看過一些spring,看到一些依賴注入的問題,其實理解沒有那麼深刻的,又回過頭來練練web項目,理解又更加深刻一些。
-
一起奮戰的兄弟們,加油!