文章目录

  • 一、项目配置问题
    • 1、【maven项目目录结构】
    • 2、【修改目录属性】
    • 3、【设置web源目录】
    • 4、【maven低版本和servlet3.0冲突】
    • 5、【控制台输出乱码解决】
  • 二、前台代码
    • 1、【发送异步请求】
    • 2、【校验手机号格式】
    • 3、【校验邮箱格式】
    • 4、【失去焦点事件】
    • 5、【前后端交互】
    • 6、【html的onclick()事件】
    • 7、【jackson】
    • 8、【checkbox】
    • 9、【获取url中拼接的参数】
  • 三、工具类的使用
  • 四、路径分发思想
  • 五、数据库问题
    • 1、【连接问题】
    • 2、【mysql语句】
    • 3、【缓存优化】
  • 六、个人对项目一些细微不足的优化
    • 1、【Alibaba Java Coding Guidelines】
    • 2、【抽取了验证码校验功能】
    • 3、【关于list==null和list.size()==0】
  • 七、实现未完成的功能
    • 1、【加载完成时,让大图成为第一张】
    • 2、【增加验证码为空的信息,并且刷新验证码】
    • 3、【增加我的收藏及分页功能】
    • 4、【增加了自动登录功能】
    • 5、【增加了热门推荐功能】
    • 6、【增加了首页三大种类旅游路线的显示功能】
    • 7、【增加了收藏排行榜展示分页以及查询功能】
    • 8、【一点小bug】
  • 八、个人反思
  • 九、后续补充
  • 十、源码下载

算是经历了整整四天吧,前两天听课跟着视频敲。后两天自己手动完成剩余的其他若干功能,一路debug过来,收获许多,在此记录。希望自己永远保持热忱,加油。

源码地址:在本文的最下方!

一、项目配置问题

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,发生异常返回falseprivate 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)    @Overridepublic 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=7location.href = "http://localhost/travel/myfavorite.html?uid=7";}
}

4、【增加了自动登录功能】

利用cookie技术,在客户端存储账号密码,实现自动登录。

5、【增加了热门推荐功能】

增加热门推荐,并连接路线具体信息。

6、【增加了首页三大种类旅游路线的显示功能】

但是上面对应的样式是真的不知道哪里改,经过debug,发现点击状态下会激活active样式是没错的,bug处在左边三栏和右边字体差了一格,希望知道怎么修复的小伙伴教教我!不然也太难受了。

注:在评论区Stevenlinc同学的提醒下,成功修改bug:修改index.html,将a链接放入span中即可。

                 <!-- Nav tabs --><ul class="jx_tabs" role="tablist"><li onclick="popu()" role="presentation" class="active"><span><a href="#popularity" aria-controls="popularity"role="tab" data-toggle="tab">人气旅游</a></span></li><li onclick="newest()" role="presentation"><span><a href="#newest" aria-controls="newest" role="tab"data-toggle="tab">最新旅游</a></span></li><li onclick="theme()" role="presentation"><span><a href="#theme" aria-controls="theme" role="tab"data-toggle="tab">主题旅游</a></span></li></ul>

在这部分基础上,又优化了部分显示:

                    <ul class="jx_tabs" role="tablist"><li onclick="popu()" role="presentation" class="active"><a href="#popularity" aria-controls="popularity"role="tab" data-toggle="tab">人气旅游</a></li><li onclick="newest()" role="presentation"><a href="#newest" aria-controls="newest" role="tab"data-toggle="tab">最新旅游</a></li><li onclick="theme()" role="presentation"><a href="#theme" aria-controls="theme" role="tab"data-toggle="tab">主题旅游</a></li></ul>

css样式改变:

        <style >.jx_tabs li  a {display: inline-block;flex: 1;flex-direction: row;position: relative;;}.jx_tabs .active a {background-color: #ffc900;color: #fff;}</style>

这部分可以下载下来代码看看效果。

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

这个部分在sql语句部分消耗了许多时间,表关系如下:

我的想法是,先根据tab_favorite中的rid进行分组,然后计算每个rid的数量,就是用户收藏的个数,按照降序排列,生成新的子查询表。然后在tab_route中寻找rid与子查询表rid相同的路线,并进行where子查询,模糊匹配路线名,以及金额大小,最后就可以获得:

由收藏次数降序排序的route,并且是完全符合搜寻条件的。

    @Overridepublic 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. 一起奋战的兄弟们,加油!


九、后续补充

有小伙伴在拿到项目之后,找到了小bug,如下:

然后出现了:xxx is not defined

因为在函数参数里面,在拼接的时候,需要用单引号将参数括起来

还可以优化一下:

var first_param = rname ===""?null:rname;
var second_param = first ===""?null:first;
var thrid_param = last ===""?null:last;
favoriteRank(null, first_param, second_param, thrid_param)

这样就可以解决了。

十、源码下载

之前码云链接失效,可能是因为我误把仓库设置成私有的了,之后我重新上传至码云仓库,需要代码的小伙伴可以进行下载。

下载地址: https://gitee.com/tqbx/itheima-travel-demo

如果觉得本文对你有帮助,请动动小手点个赞,哈哈。

【黑马旅游网】项目完结+未完成功能实现+个人总结+bug记录相关推荐

  1. 黑马旅游网项目详细思路和完整代码整理 -附源码

    黑马旅游网项目详细思路和完整代码整理 前言 由于新冠病毒的原因,无法上学.百无聊赖下自己开始看视频学习,跟着视频做完了这个项目来检验学习成果,顺便写篇博客来记录一下. 话不多说,开始正题. 文档及其源 ...

  2. 黑马旅游网项目总结与完善后端(非maven构建)

    项目介绍:仿黑马旅游网,项目不是用maven构建的项目,数据库连接池使用的是c3p0不是druid,操作操作sql用的不是jdbcTemplate而是Apache的DBUtlis工具,json工具用的 ...

  3. 黑马旅游网完整代码_GitHub - mr-yhl/tianjin_travel: 黑马旅游网项目练习

    黑马旅游网(web阶段综合练习) 第一天 页面搭建及注册功能 1 项目搭建 1.1 项目介绍 为了巩固web基础知识,提升综合运用能力,故而讲解此案例.要求,每位同学能够独立完成此案例. 1.2 技术 ...

  4. 2020黑马旅游网项目素材及源码(无废话版)

    一.成品展示 二.素材下载 链接:https://pan.baidu.com/s/1HOmZt-Di53LHeNHR0fl3-g 提取码:eixh 三.代码 <!DOCTYPE html> ...

  5. 黑马旅游网——旅游路线详情展示和旅游路线收藏功能(完结)

    旅游路线详情展示效果: 将该旅游路线的价格.商家.风景图等等详细信息展示到详情页面上: 这个功能实现起来不难,但是比较墨迹,因为这一个页面中的信息要从三张表中查询:商家信息在seller表,图片在ro ...

  6. JavaWeb黑马旅游网-学习笔记05【分类数据展示功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  7. JavaWeb黑马旅游网-学习笔记03【登陆和退出功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  8. JavaWeb黑马旅游网-学习笔记02【注册功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  9. JavaWeb黑马旅游网-学习笔记10【项目代码】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  10. 黑马旅游网综合项目-----前后端交互

    目录 前言 前台 页面展示 酒店分类 点击查看详情 ​编辑 旅游分类 查看详情 后台 页面展示 主页面 登录页面 所遇到的问题 详细代码查看 ​编辑 前言 最近做了一个JavaWeb项目-----黑马 ...

最新文章

  1. Hadoop 4、Hadoop MapReduce的工作原理
  2. linux学习——大话linux网络
  3. 数据中心建设模式变革-- 如何采用EPC模式实现快速交付?
  4. 线性变化和非线性变化
  5. oracle 数据 导出 excel 自动分多个文件,从oracle数据库中导出大量数据到excel中为什么自动分成了好几个excel文件《excel表格新手入门》...
  6. SSM框架下log4j的配置和使用
  7. 中班音乐活动 机器人_【教育生活】音乐浸润童心,专业引领成长 ——记柯桥区中心幼儿园教育集团音乐项目组教学展示与研讨活动...
  8. opencv在同一窗口打印多张图片
  9. 如何使用TensorFlow构建简单的图像识别系统(第2部分)
  10. Linux系统编程——僵尸的模拟以及僵尸进程的预防
  11. h:commandButton
  12. cartographer运行没有map_提高代码运行效率——Map的妙用
  13. 模板函数:将string类型变量转换为常用的数值类型和常用的数值类型转String
  14. Linux Interrupt——魅族内核大神文章
  15. sqlite报错database is locked
  16. html遮罩效果mask,H5案例分享:特殊形状图片之遮罩蒙版CSS3-Mask效果
  17. 小程序中如何关注公众号
  18. 自学前端到上岸工作系列之css03
  19. Altium DesignerPCB内部走线角度切换 直线切换圆弧
  20. 【跨层注意力:多层次融合】

热门文章

  1. 京东商品详情数据接口(APP端,H5端),监控京东商品历史价格及价格走势,接口代码对接教程
  2. linux 内核更改,linux_kernel修改、编译、加载
  3. CentOS7快速配置服务器网卡聚合双bond方法
  4. openharmony开发TS语言基础
  5. DTL 模板 for
  6. [HTML/CSS]Flex 布局中space-evenly 的兼容性
  7. 5月18日第壹简报,星期三,农历四月十八
  8. Sticky footer布局
  9. PHP微信公众号登录获取openid信息
  10. 操作系统——进程调度