本周主要学习了servlet的基本用法,本周以前我们一直是将Java代码写在页面里,servlet就是将这些Java代码移入java文件。
在创建servlet时我们默认勾选doGet,doPost
创建成功后生成了一个Java文件,文件中会自动有我们刚刚勾选的doGet和doPost方法
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
doGet和doPost都会有上述代码,我们可以发现这输出的都是一个网页中的基本元素这段我认为可以说是创建的servlet自己的自我介绍。因为这段代码就直接在浏览器端看到网页文件的效果,也就是可以直接在浏览器访问java文件。
一般我们在敲代码前会将这段代码删除。
Servlet目前我所知的,我自己认为web.xml是了解servlet的关键。我现在所知的servlet只是将上周讲到的java代码写入了创建的servlet中而已,本质区别不大。反而是web.xml中自动生成的代码研究了好久。
每创建一个servlet都会在web.xml中自动生成这样一段代码。在之前的学习中使用表单提交数据,只需在action属性里设置要提交到的网页名就可以了,为什么它会跑到servlet的Java文件里执行其中的Java代码,Web.xml里这两段代码给了我答案。
首先看servlet标签中的,前两行暂时没发现有什么用,主要看后面两行,servlet-name类似别名,可以任意命名,但上下两端代码的servlet-name必须一致。Servlet-class是这个命名所关联的类名,相当于路标,这个路标的目的地就是我们命名的java文件。
再来看下面的servlet-mapping,第一个是命名就不说了,第二个url-pattern中包裹的就是访问路径,这个路径就是不管从浏览器,还是在页面的form表单,使用的都是这个路径。比如form表单的action属性,必须使用url-pattern中的值才能将数据提交到该java文件。浏览器也是一样,启动服务器后访问时在url栏也必须在项目名后是它才能访问到这个servlet的Java文件并执行其中的代码。
接下来两个知识点也是关于web.xml文件的。
ServletConfig接口
servletConfig可以读取配置在web.xml中的初始化参数。
如何创建这个初始化参数:
将web.xml的窗口从Source换到Design可以看到一个servlets按钮,将其展开可以看到所有已创建的servlet文件
选择其中一个将右边的窗口下拉会看到一个Initial Parameters标题的区域,该区域中只有两个字段,Parameter Name和Parameter Value,一个相当于变量名,一个是值。
只要单击旁边的加号就能添加,这里创建的两个变量只有在该servlet中才能获取到。
前往刚刚创建初始化变量的类创建一个servletConfig对象然后获取初始化变量
ServletConfig sc = this.getServletConfig();
String uname = sc.getInitParameter("username");
String upwd = sc.getInitParameter("userpwd");
System.out.println(uname+"|"+upwd);
在控制台输出,虽然是在控制台输出但还是要在浏览器端使用url-pattern中的值访问才会引发servlet并执行其中的代码。
第二个就是同样方法创建的
ServletContext sContext = sc.getServletContext();
String defaultFace = sContext.getInitParameter("defaultFace");
System.out.println(defaultFace);
这个与上一个不同的地方就在于它可以在所有servlet中使用
过滤器和监听器
过滤器实质是在Web应用服务器上的一个Web应用组件,用于拦截客户端浏览器与目标资源的请求,并对这些请求进行一定的过滤处理,然后再发送给目标资源。
比如我们现在在使用request,response时经常要手动编码,虽然代码量不多,但显得非常麻烦,这时候就可以使用过滤对整个项目过滤,省掉几句代码。
先创建一个类,然后为它对接一个接口Filter
注意不能选错,是javax.servlet包中的Filter。
创建好之后要重写其中的三个方法init初始化方法、destroy关闭时会调用的方法,再就是最主要的doFilter方法。
其中要写入的就是过滤的代码,也类似滤网,所有项目中会被执行的代码都要经过这个滤网。现在把request和respond的编码写入
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
chain.doFilter(request, response);
最后的chain.doFilter是将过滤的最后请求转交到下一个资源,也就是继续执行接下来要执行的代码。
接下来就要区web.xml文件中配置过滤器的范围与servlet再web.xml中不同的地方只有一个就是在url-pattern,这里参数不在是用来访问“它“的路径,而是控制它过滤哪些路径下的文件,这里我们过滤的编码格式,所以设置所有
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.lb.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
监听器
所谓监听器,就是接上接口HttpSessionAttributeListener 接口的类叫做监听器,接上接口后重写其中的三个方法,attribute添加时、删除时、修改时的操作,监听器的配置很简单,只需在web.xml文件中配置其关联的类就行
在xml文件中Desgn中的listeners中输入关联的类即可启动监听器,一旦代码的功能涉及到监听器的监听范围(在session中做添加修改等功能)监听器就会执行我先事先写好的代码,比如session添加时监听器在控制台输入正在监听session中的哪个元素。
接下来就是本周一个关于连接数据的购物车实现代码
首先去数据库创建相关的表,
想形成一个完整的购物车系统,需要有一个用户,有商品,有对应用户的购物车
商品表需要有商品ID,商品名称,商品介绍,商品价格,商品图片
用户表要有用户ID,用户名,密码
购物车表要有用户ID,商品ID,数量。
在项目中建好相应的包就可以开始写功能代码了
首先是登录登录的操作,要与数据库的用户账号密码想匹配
int state = 0;
String strSql = "select * from admintbl where loginID=? and loginPWD=?";
Object[] params = new Object[2];
params[0] = user.getLoginID();
params[1] = user.getLoginPWD();
try {
ResultSet rs = DBManage.getResultSet(strSql, params);
if(rs.next()){
user.setUid(rs.getInt("uid"));
state = 1;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return state;
将sql语句带入和条件数组带入DBmanage中执行,用next方法判断它其中是否有值,有值就代表有该用户的账号密码返回state为1
User user = new User();
UserServce userService = new UserServce();
//获取页面输入的账号密码
user.setLoginID(request.getParameter("loginID"));
user.setLoginPWD(request.getParameter("loginPWD"));
//将上面代码的返回值存入现有变量
int state = userService.login(user);
HttpSession session = request.getSession();
//根据其中的值判断,若为1就跳转到主页,否则显示账号密码错误
if(state==1){
session.setAttribute("user", user);
response.sendRedirect("GoodsListAll");
}
else{
session.setAttribute("err", "账号或密码错误!");
response.sendRedirect("adminlogin.jsp");
}
然后是主页中要显示数据库中的商品。一样先从数据库查找代码开始,然后是servce到servlet,同样的套路。
//查找表中的所有商品
String strSql = "SELECT * FROM commoditytbl";
List<Goods> list = new ArrayList<Goods>();
try {
//定义集合,带入SQL语句执行后返回集合
ResultSet rs = DBManage.getResultSet(strSql);
判断集合中是否有值
while (rs.next()) {
//定义商品对象
Goods goods = new Goods();
//用rs输出为商品对象赋值,其中的参数字符串是数据库的字段名
//是用getInt还是getString根据goods对象封装的变量类型判断
goods.setGid(rs.getInt("commodityID"));//商品ID
goods.setGoodsName(rs.getString("CommodityName"));//商品名称
goods.setGoodsPrice(rs.getString("CommodityPrice"));//商品价格
goods.setGoodsDetails(rs.getString("CommodityJj"));//商品简介
goods.setGoodsimg(rs.getString("Commodityimg"));//商品的图片路径
list.add(goods);//将goods对象添加到集合list返回
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return list;
}
主页商品列表显示的servlet代码
HttpSession session = request.getSession();
GoodsServce goodsServce = new GoodsServce();
//上述代码会返回list集合
List<Goods> list =goodsServce.SelectAll();
//将其存入session便以在页面显示
session.setAttribute("goodslist", list);
//跳转到商品列表的主页
response.sendRedirect("goodslistall.jsp");
如何将session中的list显示,使用JSTL表达式,引用相关指令后使用forEach循环页面信息,设计好页面后在文本区域输出内容。
//items中的参数是session中存入的key值,var的参数是指定集合内数据的变量名称
<c:forEach items="${goodslist }" var="goods">
<img src="${goods.goodsimg }"/>//图片路径
商品名称:${goods.goodsName }
价格:${goods.goodsPrice }
</c:forEach>
这样就可以将集合中的数据显示出来
接着就要开始购物车的核心功能,对购物车的添加,删除。
从创建购物车的封装类开始,根据数据库中的,购物车类要有用户ID和商品ID及数量,这里我是直接存了一个goods对象,因为后续在购物车中显示只靠一个商品ID是远远不够的,
public class CartGoods {
private int uid; //用户ID
private Goods goods; //商品ID
private int number; //商品数量
}
然后是一个临时购物车的封装类,如果说刚刚的购物车类是存入数据库和从数据库取值专用的,这个可以叫做是临时购物车类。
public class Carts {
private List<CartGoods> goodList = new ArrayList<CartGoods>();
private double countPrice;
}
我在这个类里创建了一个集合,这个集合并不封装。这也是为什么刚刚要直接存goods商品对象,在页面上显示时,把这个集合存入session,会直接显示这个list集合,而不是从数据库读购物车的内容。
此类中还有两个方法,
//将商品放入购物车
public void add(CartGoods cartGoods){
goodList.add(cartGoods);
}
//将商品从购物车删除
public void delete(CartGoods cartGoods){
goodList.remove(cartGoods);
}
定义者此类的对象后直接调用这两个方法对集合进行增删的操作。
将主页的商品添加到临时购物车对象代码
GoodsServce goodsServce = new GoodsServce();//调用业务逻辑
HttpSession session =request.getSession();
CartGoods cartGoods = null;//先定义购物车对象
//获取传过来的商品ID
int gid = Integer.parseInt(request.getParameter("id"));
//获取当前登录的的用户ID
User user = (User)session.getAttribute("user");
String uid = user.getLoginID();
//获取传过来的价格方便计算总价
double price =Double.parseDouble(request.getParameter("price"));
//先判断session中是否存入过该商品
Carts carts =(Carts)session.getAttribute("carts");
//若为空则定义临时购物车对象
if(carts==null){
carts = new Carts();
}
boolean state = false;
//判断临时购物车对象的集合中是否有重复的商品
//GoodList中的泛型是CartGoods,若有重复的则直接将GoodList的值给CartGoods对象
for (CartGoods cg:carts.getGoodList()) {
if(cg.getGoods().getGid()==gid){
state = true;
cartGoods =cg;
break;
}
}
//若有重复的商品则number+1
if(state){
int i = cartGoods.getNumber()+1;
cartGoods.setNumber(i);
}
//没有便将商品查出信息给购物车并加入到临时购物车集合中
else{
cartGoods =new CartGoods();
Goods goods =goodsServce.selectByID(gid);
cartGoods.setUid(user.getUid());
cartGoods.setGoods(goods);
cartGoods.setNumber(1);
carts.add(cartGoods);
}
//将现在临时购物车对象保存到session以便显示
session.setAttribute("carts", carts);
//保存成功后跳转到购物车界面
response.sendRedirect("goodsCarts.jsp");
在购物车页面也使用forEach循环输出session中的carts对象
<c:forEach items="${carts.goodList}" var="cartGoods">
<img src="${cartGoods.goods.goodsimg}"/></dt>
<div>${cartGoods.goods.goodsName}</div>
<p>¥${cartGoods.goods.goodsPrice}</p>
<div >数量:${cartGoods.number}</div>
<div ><a href="GoodsDeleteServlet?gid=${cartGoods.goods.gid}" id="add">删除</a></div>
</c:forEach>
然后是购物车的删除操作,在购物车显示时使用一个a标签传值,传商品的ID
//获取页面传过来的商品ID
HttpSession session =request.getSession();
int gid = Integer.parseInt(request.getParameter("gid"));
//将session中存着的现有的购物车内容给购物车对象
Carts carts =(Carts)session.getAttribute("carts");
//在session给的值不为空的情况下使用foreach遍历集合
if(carts!=null){
for (CartGoods cg : carts.getGoodList()) {
//找到该ID对应的商品并删除它
if(cg.getGoods().getGid()==gid){
carts.delete(cg);
break;
}
}
}
//删除后更新一次session跳回购物车页面
session.setAttribute("carts", carts);
response.sendRedirect("goodsCarts.jsp");
现在完成了购物车的添加和删除的操作,但这只是存入session的假值,关闭浏览器就会消失,并没有与数据库完成交互。这就要使用到监听器,监听session中的内容,当出现用户的session键值就读取该用户购物车的内容。当session出现变动时,比如上面代码添加商品或删除商品时都会改变session对session进行一次更新,当它出现了变动就将变动的新session内容写入数据库。
监听器功能
System.out.println("开始获取该ID的购物车");
//session每进行一次添加获取session中的key值名
String name = event.getName();
//判断key值是否是存用户的key值
if(name.equals("user")){
//是就获取内容,该用户的ID
User user = (User)event.getValue();
Carts carts =new Carts();
//用ID查找其ID在数据库购物车表中对应的商品
List<CartGoods> list = cartsServce.SelectByUid(user.getUid());
for (int i = 0; i < list.size(); i++) {
//将返回的商品集合存入临时购物车的集合
carts.add(list.get(i));
}
//将集合存入session
event.getSession().setAttribute("carts", carts);
}
session变动时的代码
//1. 得到cart数据
//session变动时也将数据库购物车表中的数据更新
String name = event.getName();
if(name.equals("cart")){
//2. 保存到数据库
Carts carts = (Carts)event.getValue();
//改动后的session保存入数据库
cartsServce.save(carts);
}
执行更新数据库购物车的代码,先删除后添加
public void deleteByUid(int uid){
String sql = "delete from carttbl where uid="+uid;
Object[] params = new Object[0];
DBManage.modifyEntiy(sql, params);
}
public void add(Carts carts){
String sql = "insert into carttbl(uid,gid,number) values";
List<CartGoods> list = carts.getGoodList();
for (int i = 0; i < list.size(); i++) {
sql += "(" + list.get(i).getUid() + "," + list.get(i).getGoods().getGid() + "," + list.get(i).getNumber()+")" ;
//最后一个逗号加在最后一个的前面
if(i<list.size()-1){
sql += ",";
}
}
Object[] params = new Object[0];
DBManage.modifyEntiy(sql, params);
}