Servlet基础
1.Servlet概述
JSP的前身就是Servlet。Servlet就是在服务器端运行的一段小程序。一个Servlet就是一个Java类,并且可以通过“请求-响应”编程模型来访问的这个驻留在服务器内存的Servlet程序。
2.Tomcat容器等级
Tomcat的容器分为4个等级,Servlet的容器管理Context容器,一个Context对应一个Web工程。
3.手工编写第一个Servlet
编写一个Servlet程序大体上需要3个步骤:继承HttpServlet-->重写doGet()或者doPost()方法-->在web.xml中注册Servlet。
HttpServlet的继承关系如图:
重写doGet还是doPost方法需要根据请求方式而定。
一、编写一个类继承自HttpRequest并重写doGet(或者doPost方法)在项目的src目录下新建一个servlet.MyServlet.java
package servlet; import java.io.IOException;
import java.io.PrintWriter; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /** 继承自HttpServlet */
public class HelloServlet extends HttpServlet { /** 重写doGet方法 */
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("处理get请求。。。");
PrintWriter out = response.getWriter();
out.println("<b>HelloServlet</b>");
} /** 重写doPost方法 */
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("处理post请求。。。");
PrintWriter out = response.getWriter();
out.println("<b>HelloServlet</b>");
} }
二、在WEB-INF/web.xml中注册刚刚新建的Servlet:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list> <!-- 注册Servlet开始 -->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/servlet/HelloServlet</url-pattern>
</servlet-mapping>
<!-- 注册Servlet结束 --> </web-app>
其中servlet-name表示Servlet的名字,servlet-class要写完成的类的定义(包名.类名),url-pattern表示Servlet的路径。
在index.jsp中使用自定义的Servlet处理get和post请求。
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>手工编写的第一个Servlet</title>
</head> <body>
<h1>第一个Servlet小例子</h1><hr>
<a href="servlet/HelloServlet">get方式请求HelloServlet</a><br /><br />
<form action="servlet/HelloServlet" method="post">
<input type="submit" value="post方式请求HelloServlet" />
</form>
</body>
</html>
发布项目,运行结果:
一个Servlet可以在web.xml中配置多个映射,这样就可以在URL中使用不同的名字访问相同的Servlet。如下所示:就可以使用给人以假象——好像使用的是asp或者php或者更多的语言。
<!-- 注册Servlet开始 -->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>servlet.HelloServlet</servlet-class>
</servlet>
<!-- 一个Servlet可以配置多个映射,向下面这样配置就可以有多种方式访问Servlet了 -->
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/servlet/HelloServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/servlet/HelloServlet.asp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/servlet/HelloServlet.php</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<!-- *表示任意名称均可 -->
<url-pattern>/servlet/hello/*</url-pattern>
</servlet-mapping>
<!-- 注册Servlet结束 -->
4.使用MyEclipse编写Servlet
1.src-->new Servlet。
2.重写doGet()或者doPost()方法。
3.部署运行。
通过MyEclipse创建Servlet的时候它默认继承自HttpServlet。默认勾选覆写init()、destory()、doGet()和doPost()方法,并且自动向web.xml中注册该Servlet。
可以发现使用MyEclipse创建的Servlet它已经自动为我们生成了输出的html模板,我们只需要做很少的改动即可。
5.Servlet的执行流程和生命周期
用户点击超链接向Servlet发送请求-->服务器在web.xml中的servlet-mapping寻找与该Servlet相对应的URL地址-->找到对应的Servlet名字-->根据Servlet的名字找到和该Servlet相关的处理类-->根据请求的方式不同确定是调用doGet还是doPost方法。
一个Servlet的生命周期大致分为3个阶段:
1.客户端发送请求给服务器。
2.服务器开始接受,先判断该请求的servlet实例是否存在,如果不存在先装载一个servlet类并创建实例。
如果存在则直接调用该servlet的service方法,之后进行判断是调用 doGet方法还是doPost方法。
3.servlet创建实例后,调用init方法进行初始化。之后调用servce方法,判断是调用doGet方法还是doPost方法。
4.最后判断服务器是否关闭,如果关闭则调用destroy方法。
下面这个例子展示了Servlet的生命周期:
首先是一个Servlet:TestServlet1
package servlet; import java.io.IOException;
import java.io.PrintWriter; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class TestServlet1 extends HttpServlet { public TestServlet1() {
System.out.println("TestServlet1构造方法被执行!");
} public void destroy() {
System.out.println("TestServlet1销毁方法被执行!");
} public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { System.out.println("TestServlet1的doGet方法被执行!");
response.setContentType("text/html;charset=utf-8");
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.println("<h1>你好我是TestServlet1</h1>");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("TestServlet1的doPost方法被执行!");
doGet(request, response);// 让doPost与doGet执行相同的操作
} public void init() throws ServletException {
System.out.println("TestServlet1的初始化方法被执行!");
} }
主页index.jsp
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>Servlet的生命周期</title>
</head> <body>
<h1>Servlet的生命周期</h1><hr />
<a href = "servlet/TestServlet1">以get方式请求TestServlet1</a>
</body>
</html>
当服务器启动之后我们第一次访问index.jsp的时候,构造方法,初始化方法和doGet()方法执行
当我们再次请求该页面的时候,只有doGet()方法被执行:
服务器关闭的时候销毁方法执行。
6.Tomcat装载Servlet的3种情况
在下列时刻Servlet容器会加载Servlet:
1.Servlet容器启动时自动装载某些Servlet,实现它只需要在web.xml文件中的<servlet></servlet>之间添加以下代码:
<load-on-startup>1</load-on-startup>
其中,数字越小表示优先级越高。
例如:我们在web.xml中设置TestServlet2的优先级为1,而TestServlet1的优先级为2,启动和关闭Tomcat:
优先级高的先启动也先关闭。
2.客户端首次向某个Servlet发送请求。【例子详见Servlet生命周期的那个例子】
3.Servlet类被修改后,Tomcat容器会重新装载Servlet。
Servlet被装载后,Servlet容器会创建一个Servlet实例,并且调用Servlet的init()方法进行初始化,在Servlet的真个生命周期内init()方法只被调用一次。
7.Servlet与JSP内置对象的对应关系
8.Servlet获取表单数据
用户在reg.jsp中填写注册表单,使用post方式将数据发送到一个名称为servlet.RegServlet的Servlet处理【Servlet的doPost()方法】,Servlet将用户信息封装成一个Users对象存储在session中,讲请求转发到userinfo.jsp。在userinfo.jsp中通过<jsp:useBean>指令从session中取出保存的用户对象,通过<jsp:getPerproty>指令显示用户对象的各个字段。
package entity; import java.util.Date; /** 用户实体类 */
public class Users {
private String username;
private String password;
private String email;
private String sex;
private Date birthday;
private String[] favorites;
private String introduce; // 自我介绍
private boolean flag; // 是否接受协议 public Users() {
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} public Date getBirthday() {
return birthday;
} public void setBirthday(Date birthday) {
this.birthday = birthday;
} public String[] getFavorites() {
return favorites;
} public void setFavorites(String[] favorites) {
this.favorites = favorites;
} public String getIntroduce() {
return introduce;
} public void setIntroduce(String introduce) {
this.introduce = introduce;
} public boolean isFlag() {
return flag;
} public void setFlag(boolean flag) {
this.flag = flag;
} }
用户实体entity.Users.java
<%@ page language="java" import="java.util.*" contentType="text/html;charset=utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>用户注册</title>
<style type="text/css">
.label{
width: 20%
}
.controler{
width: 80%
}
</style>
<script type="text/javascript" src="js/Calendar3.js"></script>
</head> <body>
<h1>用户注册</h1>
<hr>
<form name="regForm" action="servlet/RegServlet" method="post" >
<table border="0" width="800" cellspacing="0" cellpadding="0">
<tr>
<td class="lalel">用户名:</td>
<td class="controler"><input type="text" name="username" /></td>
</tr>
<tr>
<td class="label">密码:</td>
<td class="controler"><input type="password" name="password" ></td> </tr>
<tr>
<td class="label">确认密码:</td>
<td class="controler"><input type="password" name="confirmpass" ></td> </tr>
<tr>
<td class="label">电子邮箱:</td>
<td class="controler"><input type="text" name="email" ></td> </tr>
<tr>
<td class="label">性别:</td>
<td class="controler"><input type="radio" name="sex" checked="checked" value="Male">男<input type="radio" name="sex" value="Female">女</td>
</tr> <tr>
<td class="label">出生日期:</td>
<td class="controler">
<input name="birthday" type="text" id="control_date" size="10"
maxlength="10" onclick="new Calendar().show(this);" readonly="readonly" />
</td>
</tr>
<tr>
<td class="label">爱好:</td>
<td class="controler">
<input type="checkbox" name="favorite" value="nba"> NBA
<input type="checkbox" name="favorite" value="music"> 音乐
<input type="checkbox" name="favorite" value="movie"> 电影
<input type="checkbox" name="favorite" value="internet"> 上网
</td>
</tr>
<tr>
<td class="label">自我介绍:</td>
<td class="controler">
<textarea name="introduce" rows="10" cols="40"></textarea>
</td>
</tr>
<tr>
<td class="label">接受协议:</td>
<td class="controler">
<input type="checkbox" name="isAccept" value="true">是否接受霸王条款
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="注册"/>
<input type="reset" value="取消"/>
</td>
</tr>
</table>
</form>
</body>
</html>
注册页reg.jsp
<%@ page language="java" import="java.util.*,java.text.*" contentType="text/html; charset=utf-8"%>
<%@ page import="entity.Users" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>用户信息显示</title>
<style type="text/css">
.title{
width: 30%;
background-color: #CCC;
font-weight: bold;
}
.content{
width:70%;
background-color: #CBCFE5;
} </style>
</head> <body>
<h1>用户信息</h1>
<hr>
<center>
<jsp:useBean id="regUser" class="entity.Users" scope="session"/>
<table width="600" cellpadding="0" cellspacing="0" border="1">
<tr>
<td class="title">用户名:</td>
<td class="content"> <jsp:getProperty name="regUser" property="username"/></td>
</tr>
<tr>
<td class="title">密码:</td>
<td class="content"> <jsp:getProperty name="regUser" property="password"/></td>
</tr>
<tr>
<td class="title">性别:</td>
<td class="content"> <jsp:getProperty name="regUser" property="sex"/></td>
</tr>
<tr>
<td class="title">E-mail:</td>
<td class="content"> <jsp:getProperty name="regUser" property="email"/></td>
</tr>
<tr>
<td class="title">出生日期:</td>
<td class="content">
<%
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String date = sdf.format(regUser.getBirthday());
%>
<%=date%>
</td>
</tr>
<tr>
<td class="title">爱好:</td>
<td class="content">
<%
String[] favorites = regUser.getFavorites();
if(favorites!=null){
for(String f:favorites)
{
%>
<%=f%>
<%
}
}
%>
</td>
</tr>
<tr>
<td class="title">自我介绍:</td>
<td class="content"> <jsp:getProperty name="regUser" property="introduce"/></td>
</tr>
<tr>
<td class="title">是否介绍协议:</td>
<td class="content"> <jsp:getProperty name="regUser" property="flag"/></td>
</tr>
</table>
</center>
</body>
</html>
用户信息显示页userinfo.jsp
package servlet; import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import entity.Users; public class RegServlet extends HttpServlet { public RegServlet() {
super();
} public void destroy() {
super.destroy(); // Just puts "destroy" string in log
} public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { doPost(request, response);//因为表单是post方式提交的
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { request.setCharacterEncoding("utf-8"); Users u = new Users();
String username,password,email,introduce,sex,isAccept;
Date birthday;
String[]favorites; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { username = request.getParameter("username");
password = request.getParameter("password");
sex = request.getParameter("sex");
email = request.getParameter("email");
introduce = request.getParameter("introduce"); if (request.getParameter("birthday")!=null&&request.getParameter("birthday").length()!=0) {
birthday = sdf.parse(request.getParameter("birthday"));
}else {
birthday = new Date();
} favorites = request.getParameterValues("favorite");//getParamterValues返回字符串数组
isAccept = request.getParameter("isAccept"); u.setUsername(username);
u.setPassword(password);
u.setEmail(email);
u.setBirthday(new Date());
u.setIntroduce(introduce);
u.setFavorites(favorites);
u.setSex(sex);
u.setFlag(isAccept!=null&&isAccept.equals("true")?true:false); //把注册成功的用户对象保存在session中
request.getSession().setAttribute("regUser", u); request.getRequestDispatcher("../userinfo.jsp").forward(request, response);
} catch (ParseException e) {
e.printStackTrace();
}
} public void init() throws ServletException {
} }
处理用户注册的Servlet:RegServlet
9.Servlet路径跳转
相对路径就是相对于当前页面的路径,绝对路径就是相对于项目根目录的路径(绝对路径需要使用到path变量)。
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>Servlet路径跳转</title>
</head> <body>
<h1>Servlet路径跳转</h1>
<!-- 使用相对路径访问HelloServlet -->
<a href="servlet/HelloServlet">使用相对路径访问servlet</a><br /> <!-- 使用绝对路径访问HelloServlet,使用path变量 -->
项目的根目录:<%=path %><br />
<a href="<%=path %>/servlet/HelloServlet">使用相对路径访问servlet</a><br />
</body>
</html>
在web.xml中注册的Servlet的路径写法是绝对路径:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name> <!-- Servlet注册开始 -->
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/servlet/HelloServlet</url-pattern><!-- Servlet的路径是绝对路径 -->
</servlet-mapping>
<!-- Servlet注册结束 --> <welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
在Servlet之中路径的跳转问题:
index.jsp中有一个链接指向servlet.TestServlet
<a href="servlet/TestServlet">访问TestServlet,跳转到Test.jsp</a>
servlet.TestServlet的doPost()和doGet()方法如下:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { doPost(request, response);
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1.使用请求重定向的方式跳转到test.jsp
// response.sendRedirect("../test.jsp");
// 也可以使用绝对路径的方式request.getContextPath
// response.sendRedirect(request.getContextPath()+"/test.jsp"); // 2.使用服务器内部跳转的方式
// request.getRequestDispatcher("../test.jsp").forward(request, response);
request.getRequestDispatcher("/test.jsp").forward(request, response);
}
在TestServlet中完成到网站根目录下的跳转有两种方式重定向和服务器内部转发,其中URL的写法也有2种方式:绝对路径和相对路径。相对路径使用..即可,而绝对路径重定向需要依赖request.getContextPath()方法取得上下文环境,而服务器内部转发中的斜线就表示项目的根目录。
开发中一般在web.xml中配置Servlet的路径为表单所在路径,这样在表单中只需要书写Servlet名字即可。在开发中应该尽量避免../的写法。
10.阶段案例——使用Servlet完成用户登录
用户名和密码都是admin,登陆成功则使用服务器内部转发到login_success.jsp,显示登录成功的用户名。登录失败则重定向到login_failure.jsp。
把login.jsp中表单的action属性改为需要处理登录的Servlet
<form action="servlet/LoginServlet" method="post" name="loginForm">
<!-- 代码省略 -->
</form>
package org.po; /**
* 用户类-javabean
*/
public class Users {
private String username;
private String password; public Users() {
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} }
用户实体类org.po.Users
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { doPost(request, response);
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { Users u = new Users();
String username = request.getParameter("username");
String password = request.getParameter("password");
u.setUsername(username);
u.setPassword(password); if (u.getUsername().equals("admin")&&u.getPassword().equals("admin")) {
request.getSession().setAttribute("username", username);
request.getRequestDispatcher("/login_success.jsp").forward(request, response);
}else {
response.sendRedirect("../login_failure.jsp");
}
}
处理用户登录的LoginServlet
Servlet高级
1.获取初始化参数
在web.xml中配置Servlet时,可以配置一些初始化参数。而在Servlet中可以通过ServletConfig接口提供的方法来取得这些参数。
1.首先在index.jsp中建立一条超链接指向servlet.GetInitParameterServlet。
<a href = "servlet/GetInitParameterServlet">获取Servlet的初始化参数</a>
2.在web.xml中配置该Servlet的初始化参数:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name> <!-- 注册Servlet开始 -->
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>GetInitParameterServlet</servlet-name>
<servlet-class>servlet.GetInitParameterServlet</servlet-class> <!-- 配置用户名初始化参数 -->
<init-param>
<param-name>username</param-name>
<param-value>admin</param-value>
</init-param>
<!-- 配置密码初始化参数 -->
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param> </servlet>
<!-- 注册Servlet结束 --> <servlet-mapping>
<servlet-name>GetInitParameterServlet</servlet-name>
<url-pattern>/servlet/GetInitParameterServlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
web.xml
3.在GetInitParameterServlet的init方法中使用this.getgetInitParameter(String name)方法获得初始化参数:
package servlet; import java.io.IOException;
import java.io.PrintWriter; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class GetInitParameterServlet extends HttpServlet {
/* 封装属性 */
private String username ;
private String password ; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public GetInitParameterServlet() {
super();
} public void destroy() {
super.destroy(); // Just puts "destroy" string in log
} public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { doPost(request, response);
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { response.setContentType("text/html;charset=utf-8");
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.println("从web.xml中获取初始化参数,用户名:"+username+"<br />");
out.println("从web.xml中获取初始化参数,密码:"+password);
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
} /** 可以在Servlet的init方法中获得web.xml中配置的初始化参数 */
public void init() throws ServletException {
this.setUsername(this.getInitParameter("username"));
setPassword(getInitParameter("password"));
} }
servlet.GetInitParameterServlet.java
运行结果:
2.MVC模式
MVC旨在分离模型、视图、控制。是分层思想的一种体现。
结构图如下:
1.浏览器发送请求被控制器接收(Servlet)。
2.由控制器实例化一个模型层对象(JavaBean),模型层访问EIS(企业信息系统,就是DB)。EIS将结果返回给JavaBean,JavaBean将结果返回给控制层Servlet。
3.控制层根据模型层返回的结果选择合适的视图给用户呈现。
3.Model2简介
Model2实际上就是JSP(V)+Servlet(C)+JavaBean(M),是MVC设计思想。
4.阶段项目:使用MVC实现购物车
JSP(View)+Servlet(Control)+ dao(Model)。
已有的商品实体类:
package entity; /**
* 商品类(与DB中的表名一致)-javabean 属性和DB中表的字段完全一致
*/
public class Items {
private int id;
private String name;
private String city;
private int price;
private int number;
private String picture; public Items() {
} public Items(int id, String name, String city, int price, int number,
String picture) {
super();
this.id = id;
this.name = name;
this.city = city;
this.price = price;
this.number = number;
this.picture = picture;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getCity() {
return city;
} public void setCity(String city) {
this.city = city;
} public int getPrice() {
return price;
} public void setPrice(int price) {
this.price = price;
} public int getNumber() {
return number;
} public void setNumber(int number) {
this.number = number;
} public String getPicture() {
return picture;
} public void setPicture(String picture) {
this.picture = picture;
} @Override
public String toString() {
return "商品编号:"+this.getId()+",商品名称:"+this.getName();
}
}
Items.java
实现阶段:创建购物车类-->编写Servlet-->创建页面层。
1.购物车类Cart的设计:
购物车有2个属性,分别是购买商品的集合和商品的总价格。
购物车的方法有3个:添加商品、删除商品、计算商品的总价格。
那么问题来了,我们应该使用何种集合来存储用户购买的商品?我们可以使用Map类型,键是商品对象,值是该种商品的数量。
实现如下:
package entity; import java.util.HashMap;
import java.util.Iterator;
import java.util.Set; /** 购物车类 */
public class Cart {
private HashMap<Items, Integer> goods;// 商品信息以键值对的方式保存在hashMap中
private double totalPrice; public Cart() {
// 在构造方法中初始化属性
goods = new HashMap<Items, Integer>();
totalPrice = 0;
} public HashMap<Items, Integer> getGoods() {
return goods;
} public void setGoods(HashMap<Items, Integer> goods) {
this.goods = goods;
} public double getTotalPrice() {
return totalPrice;
} public void setTotalPrice(double totalPrice) {
this.totalPrice = totalPrice;
} /**
* 添加商品到购物车
* @param items:商品对象
* @param number:商品数量
*/
public boolean addGoodsInCart(Items item, int number) {
goods.put(item, number);
calcTotalPrice();// 重新计算购物车的总金额
return true;
} /**
* 从购物车中删除商品
*/
public boolean removeGoodsFromCart(Items item) {
goods.remove(item);
calcTotalPrice();// 重新计算购物车的总金额
return true;
} /** 计算购物车的总金额 */
public double calcTotalPrice() {
double sum = 0;
Set<Items> keys = goods.keySet();// 获得键的集合
Iterator<Items> iterator = keys.iterator();
while (iterator.hasNext()) {
Items items = (Items) iterator.next();
sum += items.getPrice() * goods.get(items);
}
this.setTotalPrice(sum);// 设置总金额
return sum;
}
}
Cart.java
接下来测试上面的购物车类:
直接在给Cart类中编写main方法测试购物车类;
public static void main(String[] args) {
//创建2个商品对象
Items i1 = new Items(1, "沃特篮球鞋", "温州", 200, 500, "001.jpg");
Items i2 = new Items(2, "李宁运动鞋", "广州", 300, 500, "002.jpg");
Items i3 = new Items(1, "沃特篮球鞋", "温州", 200, 500, "001.jpg"); //创建购物车对象
Cart cart = new Cart();
cart.addGoodsInCart(i1, 1);
cart.addGoodsInCart(i2, 2);
cart.addGoodsInCart(i3, 3);//再买3双沃特篮球鞋 //购物车中商品的集合
Set<Map.Entry<Items, Integer>>items = cart.getGoods().entrySet();
for (Map.Entry<Items, Integer> entry : items) {
System.out.println(entry);
}
System.out.println("买4双沃特篮球鞋和2双李宁运动鞋之后,购物车总价格:"+cart.getTotalPrice());;
}
运行结果:
虽然总金额的计算没有问题,但是用户体验不太好,购物车中中出现了相同的商品记录(应该是需要合并的)。
一种方式是重写Items类的hashCode和equals方法。重写之后再次运行程序:
虽然商品的记录在购物车中不会重复,但是购买的数量却不对了,沃特篮球鞋的后面的一条记录替换掉了前面的记录。
解决方案:此时只要修改购物车的addGoodsInCart()方法就行了:在加入新的商品之前先判断如果已经添加了相同的商品,只需要修改商品的数量即可:
public boolean addGoodsInCart(Items item, int number) {
//如果商品已经在购物车中只需要修改商品数量即可【原有数量+新加入的数量】
if (goods.containsKey(item)) {
goods.put(item, goods.get(item)+number);
}else {
goods.put(item, number);
}
calcTotalPrice();// 重新计算购物车的总金额
return true;
}
运行结果:
2.Servlet类的设计
由Servlet类调用购物车类的代码实现购物功能。
package servlet; import java.io.IOException;
import java.io.PrintWriter; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import dao.ItemsDAO;
import entity.Cart;
import entity.Items; public class CartServlet extends HttpServlet { private String action;//购物车的动作(添加?删除?显示?商品)
private ItemsDAO idao = new ItemsDAO();//商品业务逻辑类的对象 public CartServlet() {
super();
} public void destroy() {
super.destroy(); // Just puts "destroy" string in log
} public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
if (request.getParameter("action")!=null) {
this.action = request.getParameter("action"); if (action.equals("add")) {//添加商品进购物车
if(addToCart(request,response)){
request.getRequestDispatcher("/add_to_cart_success.jsp").forward(request, response);
}else {
request.getRequestDispatcher("/add_to_cart_failure.jspp").forward(request, response);
}
}
if (action.equals("show")) {//显示购物车
request.getRequestDispatcher("/cart.jsp").forward(request, response);
}
if(action.equals("delete")){//从购物车中删除商品
deleteFromCart(request,response);
request.getRequestDispatcher("/cart.jsp").forward(request, response);
}
}
} /** 从购物车中删除商品 */
private boolean deleteFromCart(HttpServletRequest request,
HttpServletResponse response) {
String id = request.getParameter("id");
Cart cart = (Cart) request.getSession().getAttribute("cart");
Items item = idao.getItemsById(Integer.parseInt(id));
if (cart.removeGoodsFromCart(item)) {
return true;
}else {
return false;
}
} /** 添加商品进购物车 */
private boolean addToCart(HttpServletRequest request,
HttpServletResponse response) {
String id = request.getParameter("id");
String number = request.getParameter("num");
Items item = idao.getItemsById(Integer.parseInt(id)); //是否是第一次给购物车添加商品?【给session中创建一个购物车对象】
if (request.getSession().getAttribute("cart")==null) {
Cart cart= new Cart();
request.getSession().setAttribute("cart", cart);
} //获得Session中保存的购物车对象
Cart cart = (Cart) request.getSession().getAttribute("cart");
if (cart.addGoodsInCart(item, Integer.parseInt(number))) {
return true;
}else {
return false;
}
} public void init() throws ServletException {
} }
CartServlet
3.界面层在details.jsp页面中显示购物车。
<%@page import="entity.Items"%>
<%@page import="dao.ItemsDAO"%>
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!-- 显示商品详情,并在右侧显示商品的浏览记录(最近5条记录) -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>欢迎光临网上商城</title>
<link href="css/main.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="js/lhgcore.js"></script>
<script type="text/javascript" src="js/lhgdialog.js"></script>
<style type="text/css">
div{
float:left;
margin-left: 30px;
margin-right:30px;
margin-top: 5px;
margin-bottom: 5px;
}
div dd{
margin:0px;
font-size:10pt;
}
div dd.dd_name{
color:blue;
}
div dd.dd_city{
color:#000;
}
div #cart{
margin:0px auto;
text-align:right;
}
span{
padding:0 2px;border:1px #c0c0c0 solid;cursor:pointer;
}
a{
text-decoration: none;
}
</style>
<script type="text/javascript">
function selflog_show(id) {
var num = document.getElementById("number").value;
J.dialog.get({id: 'haoyue_creat',title: '购物成功',width: 600,height:400, link: '<%=path%>/servlet/CartServlet?id='+id+'&num='+num+'&action=add', cover:true});
}
function add(){
var num = parseInt(document.getElementById("number").value);
if(num<100){
document.getElementById("number").value = ++num;
}
}
function sub(){
var num = parseInt(document.getElementById("number").value);
if(num>1){
document.getElementById("number").value = --num;
}
}
</script>
</head> <body>
<h1>商品详情</h1><hr>
<a href="index.jsp">首页</a> >> <a href="index.jsp">商品列表</a><hr/>
<center>
<table width="750" height="60" cellpadding="0" cellspacing="0" border="0">
<tr>
<!-- 商品详细信息 -->
<%
Items item = new ItemsDAO().getItemsById(Integer.parseInt(request.getParameter("id")));
if(item!=null){
%>
<td width="70%" valign="top">
<table>
<tr>
<td rowspan="4"><img src="data:images/<%=item.getPicture()%>" width="200" height="160"/></td>
</tr>
<tr>
<td><b><%=item.getName() %></b></td>
</tr>
<tr>
<td>产地:<%=item.getCity() %></td>
</tr>
<tr>
<td>价格:¥<font color="red"><b><%=item.getPrice() %></b></font></td>
</tr>
<tr>
<td>
购买数量:<span id="sub" onclick="sub();">-</span>
<input type="text" id="number" name="number" value="1" size="2"/>
<span id="add" onclick="add();">+</span>
</td>
</tr>
</table>
<div id="cart">
<img src="data:images/buy_now.png">
<a href="javascript:selflog_show(<%=item.getId()%>)"><img src="data:images/in_cart.png" /></a>
<a href="servlet/CartServlet?action=show"><img src="data:images/view_cart.jpg"/></a>
</div>
</td>
<%
}
%>
<!-- 取得Cookie -->
<%
String list = "";
// 从客户端获得Cookie集合
Cookie[]cookies = request.getCookies();
if(cookies!=null&&cookies.length>0){
for(Cookie c:cookies){
if(c.getName().equals("ListViewCookie")){
list = c.getValue();
} String[] arr = list.split(",");
// 相同商品只在浏览记录中存放一次
if(Arrays.binarySearch(arr, request.getParameter("id"))<0){
list += request.getParameter("id") + ",";
}
// 如果浏览记录超过1000条,则清空Cookie
if(arr!=null&&arr.length>1000){
list = "";// 清零-置空串
}
Cookie cookie = new Cookie("ListViewCookie",list);
response.addCookie(cookie);
}
}
%> <!-- 浏览过的商品 -->
<td width="30%" bgcolor="#EEE" align="center">
<br /><b>您浏览过的商品</b><br />
<!-- 循环开始 -->
<%
ArrayList<Items>itemsList = new ItemsDAO().getViewList(list);
if(itemsList!=null&&itemsList.size()>0){
for(Items i:itemsList){
%>
<div>
<dl>
<dt><img src="data:images/<%=i.getPicture() %>" width="120" height="90" border="1" /></dt>
<dd class="dd_name"><%=i.getName() %></dd>
<dd class="dd_city">产地:<%=i.getCity() %> 价格:¥<%=i.getPrice() %></dd>
</dl>
</div>
<%
}
}
%>
<!-- 循环结束 -->
</td>
</tr>
</table>
</center>
</body>
</html>
details.jsp
项目的完整地址:https://git.oschina.net/gaopengfei/JavaWebShoppingDemoByMVC.git