一、 项目导入
-
点击侧边的maven(如果没有可以在view–>tool window 中找到),点击加号,选择文件中的pom.xml文档即可导入
-
首先预览一下静态页面
启动方式1:
启动方式2:添加启动方式,点击绿色三角形启动
二、技术选型
-
web:
servlet:前端控制器
HTML:数据展示
filter:过滤器
BeanUtils:数据封装
Jackson:json序列化工具 -
service:
Javaemail:Java发送文件
redis:nosql内存数据
jedis:Java的redis的客户端 -
Dao
Mysql:数据库
Druid:数据库连接池
jdbcTemplate:jdbc的工具类
三、创建数据库
四、实现登录、注册、退出
各层调用图:
注册:在html利用js完成表单校验,使用ajax完成表单提交
注册成功,跳转成功页面
注册失败,跳转失败页面
servlet:RegisterServlet
(0.设置编码已经利用统一过滤器完成)
1.获取数据
2.封装User对象
3.调用service完成注册
4.根据service返回,提示信息:
将提示信息信息转为json
设置相应头:contentType
service:
registerUser(User user)
调用dao根据用户名查询用户
存在,返回false
不存在,调用dao保存用户信息
Dao:
1.根据用户名查询用户信息 findByUsername(string username);
2.保存用户信息 save(User user)
异步Ajax提交表单:
$(this).serialize()将表单中的数据提交成 username=xxx&&password=123 的格式
使用异步提交是为了获取服务器响应的数据,前台使用html作为视图层,不能直接从servlet相关的域对象获取值,只能通过Ajax获取
**
在编写的时候,写错了一个愚蠢的问题,导致无法访问主页,就是注释名称写错了,webServlet写成了webFilter导致无法访问网页404
**
login.jsp
简单的jquery登录验证
<script type="text/javascript">
//检查用户名格式
function checkUsername() {
var username = $("#username").val();
var reg_username = /^[A-Za-z_0-9]{4,20}$/;
var flag = reg_username.test(username);
if(flag){
$("#username").css("border","");
}
else{
alert("用户名格式错误");
$("#username").css("border","1px solid red");
//$("#addusername").html("用户名格式错误"); //要记得添加一个新的html格式
}
return flag;
}
//检查密码格式
function checkPassword(){
var password = $("#password").val();
var reg_password = /^[A-Za-z_0-9]{4,20}$/;
var flag = reg_password.test(password);
if(flag){
$("#password").css("border","");
}
else{
alert("密码格式错误");
$("password").css("border","1px solid red");
//$("#addusername").html("用户名格式错误"); //要记得添加一个新的html格式
}
return flag;
}
//提交时检查是否正确
$(function () {
$("#form").submit(function () {
if(checkPassword()&&checkUsername()){
return true;
}
return false;
})
//失去焦点时,判断格式
$("#username").blur(checkUsername);
$("#password").blur(checkPassword);
})
</script>
在body中添加form表单将值传入Servlet
<form id="form" name="form" action="indexServlet">
<div>
用户名:
<input type="text" id="username" name="username" placeholder="请输入用户名">
</div>
<div>
密码:
<input type="text" id="password" name="password" placeholder="请输入密码">
</div>
<div>
<input type="submit" id="login" name="login" value="登录">
</div>
</form>
可以用${error}来接收后台传递的错误信息
<!--得到从后台得到的错误信息-->
<div>${error}</div>
LoginServlet
//设置编码
req.setCharacterEncoding("utf-8");
//获取用户输入的数据
String username = req.getParameter("username");
String password = req.getParameter("password");
//判断用户名和密码是否正确
if(username.equalsIgnoreCase("zhangsan")&&password.equalsIgnoreCase("123456")){
//用户名和密码正确,跳转至主页面
req.getRequestDispatcher("/home.jsp");
}else{//用户名和密码错误,存储错误信息
req.setAttribute("error","用户名或密码错误");
//携带错误信息,转发到登录页面
req.getRequestDispatcher("/index.jsp").forward(req,resp);
}
五、注册页面的邮件激活
原因:为了保证用户填写的邮箱是正确的。可以推送广告
发送邮件:利用emailutil工具类编写
激活邮件:
激活对于数据库来说,就是将status 状态改为激活态
邮件激活分析:
不要再return下写代码,Java报unreachable statement错误
详细看连接:https://blog.csdn.net/qq_33915826/article/details/79246482
六、登录
登录之后,动态展示个人用户名称
header.html
<script>
$(function () {
$.get("findUserServlet",{},function (data) {
//{uid:1,name:"yangcichen"}
var msg = "欢迎回来,"+data.name;
$("#span_username").html(msg);
})
})
</script>
异步交互
当页面整个访问完成之后,用ajax访问服务器查询出信息
七、退出
首先,应该思考一下,什么时候称作登录成功?
登录成功,就是session中有user
那么退出功能实现:
header.html
<a href="javascript:location='exitServlet';">退出</a>
1.访问servlet,将session销毁
req.getSession().invalidate();
2.跳转到登录界面跳转页面,采用重定向(路径写法为虚拟目录)
resp.sendRedirect(req.getContextPath()+"/login.html");
八、优化Servlet——BaseServlet
优化方式:减少servlet的数量,将其优化为一个模块一个Servlet
写一个模块servlet 让各个功能的servlet继承
模块servlet继承HTTPServlet
“this”–谁调用我,我代表谁
报错说明没有匹配的方法:
原因:方法的声明为protected,访问的时候没有权限
解决方法:
①在访问的时候忽略访问权限修饰符,此方法会忽略所有的方法
//忽略访问权限修饰符
Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
//执行方法
//暴力反射
method.setAccessible(true);
method.invoke(this,req,resp);
②直接将方法权限修饰符改为public,这有利于其他的私有方法被保护
又犯了一个低级错误——findOne名称写错,导致浏览器找不到
九、旅游数据的显示
分类数据展示:
原来是假数据,后期通过从后台动态生成
<div class="navitem">
<ul class="nav">
<li class="nav-active"><a href="index.html">首页</a></li>
<li><a href="route_list.html">门票</a></li>
<li><a href="route_list.html">酒店</a></li>
<li><a href="route_list.html">香港车票</a></li>
<li><a href="route_list.html">出境游</a></li>
<li><a href="route_list.html">国内游</a></li>
<li><a href="route_list.html">港澳游</a></li>
<li><a href="route_list.html">抱团定制</a></li>
<li><a href="route_list.html">全球自由行</a></li>
<li><a href="favoriterank.html">收藏排行榜</a></li>
</ul>
</div>
数据库中信息:
进行代码优化:页面分类数据在每一次页面加载后都会重新要求数据库来加载,对数据库的压力比较大,而且分类的数据不会经常产生变化,所以可以用redis来缓存这个数据。
期望数据库中存储的顺序,就是展示的数据
十、旅游线路分页展示
点击了不同的分类后,看到的旅游路线不一致。
分类和旅游线路是一对多
如何携带cid:
//后台service
Set<Tuple> categorys = jedis.zrangeWithScores("category", 0, -1);
//2.判断查询的集合是否为空
List<Category> cs=null;
//2.1为空,第一次访问
if(categorys==null||categorys.size()==0){
//从数据库查询
cs = categoryDao.findAll();
//将集合存储到redis中的category的key
for (int i = 0; i < cs.size(); i++) {
//存储zadd
jedis.zadd("category",cs.get(i).getCid(),cs.get(i).getCname());
}
}else{
//2.2不为空,将set数据存入list
//前端需要List,但是此方法中返回set,进行格式转换
cs=new ArrayList<Category>();
for (Tuple tuple:categorys) {
Category category = new Category();
category.setCname(tuple.getElement());
category.setCid((int)tuple.getScore());
cs.add(category);
}
}
前端:
for(var i=0;i<data.length;i++){
var li = '<li><a href="favoriterank.html?cid='+data[i].cid+'">'+data[i].cname+'</a></li>';
lis += li;
}
如何取cid:
<script>
$(function () {
var search = location.search;
//alert(search);
//切割获取到的字符串[?cid=5] 拿到5
var cid = search.split("=")[1];
})
</script>
根据cid获取不同的旅游线路数据:
十一、旅游线路名称查询
1.查询参数的传递
通过从route_list?cid=5 传递 route_list?cid=5&rname=xxx
传递xxx
首先获取到用户输入的rname
/*header.html*/
$("#search-button").click(function () {
//后台根据这个名称进行模糊匹配
var rname = $("#search_input").val();
//alert(rname);
var cid = getParameter("cid");
//alert(cid);
//跳转路径http://localhost/travel/route_list.html?cid=5
//再拼接上rname=xxx
location.href="http://localhost/travel/route_list.html?cid="+cid+"&rname="+rname;
})
/*route_list.html*/
//getParameter()方法为自己封装的字符切割函数
//获取cid
var cid = getParameter("cid");
//获取rname
var rname = getParameter("rname");
if(rname){
//解码方法,获取到的rname为url需要解码
rname=window.decodeURIComponent(rname);
}
2.修改之前的后台代码
Servlet、Service、Dao
tomcat7 从前端传值到后台乱码,解决办法:
//tomcat7,get请求出现乱码
rname = new String(rname.getBytes("iso-8859-1"),"utf-8");
出现了直接点击相关路线切换线路查询不到数据的问题:
传值load(5,2,‘null’)
解决办法链接
当rname为空时,前端传给RouteServlet的是“null”字符串,而不是null
所以在RouteDaoImpl中的判断参数是否有值的地方把rname当成“null”关键词(有值)来查询,而数据库没有关键词为“null”的旅游路线,所以返回0条记录,产生错误。
与之前判断为cid为’‘null’'时,有相似之处。
十二、查看旅游路线详情展示
出现图片展示有误:
通过前端断点调试发现标签使用了style="display:none;属性
如果删除其属性,图片可以正常显示,但是显示的数据css不正确
判断一下i 即每页的展示显示4个,超过4个进行隐藏
//遍历routeImgList
for (var i = 0; i < route.routeImgList.length; i++) {
if(i>=4){
var astr='<a title="" class="little_img" data-bigpic="'+route.routeImgList[i].bigPic+'" style="display:none;">\n' +
' <img src="'+route.routeImgList[i].smallPic+'">\n' +
' </a>'
}else {
var astr='<a title="" class="little_img" data-bigpic="'+route.routeImgList[i].bigPic+'" >\n' +
' <img src="'+route.routeImgList[i].smallPic+'">\n' +
' </a>'
}
ddstr+=astr;
}
十三、旅游路线收藏功能
页面加载完成后,发送ajax请求,查询数据库查看用户是否收藏过该线路
根据查询结果展示不同的样式
收藏字数的动态展示
点击收藏按钮:
给按钮绑定单击事件,点击了按钮调用js中的addFavorite()方法:
<a class="btn" id="favorite" onclick="addFavorite();"><i class="glyphicon glyphicon-heart-empty" ></i>点击收藏</a>