Top

JAVA SERVLETJSP DAY02

  1. 员工管理——使用JDBC删除员工信息
  2. 员工管理——使用JDBC修改员工信息
  3. 容器对URI的处理
  4. 员工管理——单一控制器实现员工信息管理
  5. Servlet的生命周期
  6. ServletContext对象(Servlet上下文对象)
  7. Servlet线程安全

1 员工管理——使用JDBC删除员工信息

1.1 问题

为员工管理添加删除的功能,即在查询结果页面中提供删除的操作链接,并实现在删除后回到查询页面的效果。如图-1

图- 1

1.2 方案

在查询的Servlet中,输出每一行员工信息的同时,多输出一个列,内容为超链接,文字显示为“删除”字样,隐含的超链接地址为删除对应动作的Servlet,并且为了区分每一个员工信息,在向删除地址发出请求的同时,传递一个当前选中的员工的id号,以get的方式提交给服务器,服务器获取该参数,依据id构建删除语句并执行,执行之后使用重定向技术请求列表页面,重新查询,生成新的查询结果页面。

1.3 步骤

步骤一:修改ListEmpServlet,添加删除链接

如图-2所示,输出“删除”超链接

图- 2

步骤二:新建DeleteEmpServlet类,处理删除动作

如图-3所示,获取请求参数值id,构建删除语句并执行

图- 3

步骤三:修改web.xml,配置DeleteEmpServlet

1.4 完整代码

ListEmpServlet.java文件代码:

package emp;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ListEmpServlet extends HttpServlet {
	/**
	 * 查询员工信息列表
	 */
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		// 设置输出流编码
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		// JDBC查询员工信息
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			Class.forName("oracle.jdbc.OracleDriver");
			conn = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:xe", "xxx", "xxx");
			stmt = conn.prepareStatement("select * from t_emp");
			rs = stmt.executeQuery();
			out.println("<html><head></head><body style='font-size:30px'>");
			out.println("<table border='1' cellspacing='0' cellpadding='0' width='600px'>");
			out.println("<caption>员工信息</caption>");
			out.println("<tr><td>编号</td><td>姓名</td><td>薪水</td><td>年龄</td><td>操作</td></tr>");
			while (rs.next()) {
				int id = rs.getInt("id");
				String name = rs.getString("name");
				double salary = rs.getDouble("salary");
				int age = rs.getInt("age");
				out.println("<tr>");
				out.println("<td>" + id + "</td>");
				out.println("<td>" + name + "</td>");
				out.println("<td>"+ salary + "</td>");
				out.println("<td>" + age + "</td>");
				out.println("<td><a href='delete?id="+id+"' " +
						"onclick=\"return confirm('是否确定删除"+name+"');\">删除</a></td>");
				out.println("</tr>");
			}
			out.println("</table></body></html>");
		} catch (Exception e) {
			e.printStackTrace();
			out.println("系统异常,请重试");
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

DeleteEmpServlet.java文件代码:

package emp;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DeleteEmpServlet extends HttpServlet{
	/**
	 * 实现删除员工信息的操作
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//获取请求参数值中的id信息
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out =response.getWriter();
		int id = Integer.parseInt(request.getParameter("id"));		
		//连接数据库,执行删除操作
		Connection conn = null;
		PreparedStatement stmt = null;
		try {
			Class.forName("oracle.jdbc.OracleDriver");
			conn = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:xe", "x", "x");
			stmt = conn.prepareStatement("delete from t_emp where id=?");
			stmt.setInt(1, id);
			stmt.executeUpdate();
			response.sendRedirect("list");
		} catch (Exception e) {
			e.printStackTrace();
			out.println("Error");
		} finally{
			if(conn != null){
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

web.xml文件代码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	
	<!-- 增加员工 -->
	<servlet>
		<servlet-name>addEmpServlet</servlet-name>
		<servlet-class>emp.AddEmpServlet</servlet-class>
	</servlet>
	<!-- 查询员工信息 -->
	<servlet>
		<servlet-name>listEmpServlet</servlet-name>
		<servlet-class>emp.ListEmpServlet</servlet-class>
	</servlet>
	<!-- 删除员工 -->
	<servlet>
		<servlet-name>deleteEmpServlet</servlet-name>
		<servlet-class>emp.DeleteEmpServlet</servlet-class>
	</servlet>

	<!-- add -->
	<servlet-mapping>
		<servlet-name>addEmpServlet</servlet-name>
		<url-pattern>/add</url-pattern>
	</servlet-mapping>
	<!-- list -->
	<servlet-mapping>
		<servlet-name>listEmpServlet</servlet-name>
		<url-pattern>/list</url-pattern>
	</servlet-mapping>
	<!-- delete -->
	<servlet-mapping>
		<servlet-name>deleteEmpServlet</servlet-name>
		<url-pattern>/delete</url-pattern>
	</servlet-mapping>
</web-app>

2 员工管理——使用JDBC修改员工信息

2.1 问题

为员工管理实现修改功能。查询结果中具备修改的链接,点击后将选中的员工信息加载到表单中,修改完毕后点击“修改”按钮,回到查询页面,查看修改后的结果。如图-4

//

图- 4

2.2 方案

在查询页面增加“修改”的超链接,并按照所选员工的id查询出一条数据,加载到表单中。当修改结束后,点击表单的“修改”那妞时,不仅仅提交表单中的修改数据,同时使用隐藏表单域,将存储的该员工id信息一同提交,服务器端收到数据后构建更新语句并执行,执行结束后使用重定向技术重新请求查询页面,生成新的查询结果,确定更新操作已成功。

2.3 步骤

步骤一:修改ListEmpServlet,增加“修改”链接

如图-5所示为增加的修改链接

图- 5

步骤二:新建LoadEmpServlet.java文件

获取请求参数值id,查询数据并构建表单显示数据,如图-6:

图- 6

步骤三:新建ModifyEmpServlet.java文件

图 - 7

步骤四:修改web.xml文件实现配置

图 - 8

2.4 完整代码

ListEmpServlet.java文件:

package emp;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ListEmpServlet extends HttpServlet {
	/**
	 * 查询员工信息列表
	 */
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		// 设置输出流编码
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		// JDBC查询员工信息
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			Class.forName("oracle.jdbc.OracleDriver");
			conn = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:xe", "xxx", "xxx");
			stmt = conn.prepareStatement("select * from t_emp");
			rs = stmt.executeQuery();
			out.println("<html><head></head><body style='font-size:30px'>");
			out.println("<table border='1' cellspacing='0' cellpadding='0' width='600px'>");
			out.println("<caption>员工信息</caption>");
			out.println("<tr><td>编号</td><td>姓名</td><td>薪水</td><td>年龄</td><td>操作</td></tr>");
			while (rs.next()) {
				int id = rs.getInt("id");
				String name = rs.getString("name");
				double salary = rs.getDouble("salary");
				int age = rs.getInt("age");
				out.println("<tr>");
				out.println("<td>" + id + "</td>");
				out.println("<td>" + name + "</td>");
				out.println("<td>"+ salary + "</td>");
				out.println("<td>" + age + "</td>");
				out.println("<td><a href='delete?id="+id+"' " +
						"onclick=\"return confirm('是否确定删除"+name+"');\">删除</a>");
				out.println("<a href='load?id="+id+"'>修改</a></td>");
				out.println("</tr>");
			}
			out.println("</table></body></html>");
		} catch (Exception e) {
			e.printStackTrace();
			out.println("系统异常,请重试");
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

LoadEmpServlet.java文件:

package emp;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoadEmpServlet extends HttpServlet {
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		// 获取请求参数值id
		int id = Integer.parseInt(request.getParameter("id"));
		// 根据Id查询数据库获取员工信息
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			Class.forName("oracle.jdbc.OracleDriver");
			conn = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:xe", "xxx", "xxx");
			stmt = conn.prepareStatement("select * from t_emp where id=?");
			stmt.setInt(1, id);
			rs = stmt.executeQuery();
			out.println("<html><head></head><body style='font-size:30px'>");
			if (rs.next()) {
				String name = rs.getString("name");
				double salary = rs.getDouble("salary");
				int age = rs.getInt("age");
				out.println("<form action='modify' method='post'>");
				out.println("编号:" + id + "<br>");
				out.println("<input type='hidden' name='id' value='"+id+"'/><br>");
				out.println("姓名:<input name='name' value='"+name+"'/><br>");
				out.println("薪水:<input name='salary' value='"+salary+"'/><br>");
				out.println("年龄:<input name='age' value='"+age+"'/><br>");
				out.println("<input type='submit' value='修改'/>");
				out.println("</form>");
			}
			out.println("</body></html>");
		} catch (Exception e) {
			e.printStackTrace();
			out.println("系统异常,请重试");
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

ModifyEmpServlet.java文件

package emp;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ModifyEmpServlet extends HttpServlet{
	/**
	 * 增加员工信息
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//0.解决中文乱码问题
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		//1.获取请求参数值
		int id = Integer.parseInt(request.getParameter("id"));
		String name = request.getParameter("name");
		double salary = Double.parseDouble(request.getParameter("salary"));
		int age = Integer.parseInt(request.getParameter("age"));
		//2.数据库操作
		Connection conn = null;
		PreparedStatement stmt = null;
		try {
			Class.forName("oracle.jdbc.OracleDriver");
			conn = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:xxx", "xuze", "xxx");
			stmt = conn.prepareStatement("update t_emp set name=?,salary=?,age=? where id=?");
			stmt.setString(1, name);
			stmt.setDouble(2, salary);
			stmt.setInt(3, age);
			stmt.setInt(4, id);
			stmt.executeUpdate();
			response.sendRedirect("list");
		} catch (Exception e) {
			e.printStackTrace();
			out.print("系统出现问题,稍后重试<br><p><a href='list.do'>员工信息列表</a></p>");
		} finally{
			if(stmt!=null){
				try {
					stmt.close();
				} catch (SQLException e) {					
					e.printStackTrace();
				}
			}
			if(conn!=null){
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}	
		
	}
}

web.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	
	<!-- 增加员工 -->
	<servlet>
		<servlet-name>addEmpServlet</servlet-name>
		<servlet-class>emp.AddEmpServlet</servlet-class>
	</servlet>
	<!-- 查询员工信息 -->
	<servlet>
		<servlet-name>listEmpServlet</servlet-name>
		<servlet-class>emp.ListEmpServlet</servlet-class>
	</servlet>
	<!-- 删除员工 -->
	<servlet>
		<servlet-name>deleteEmpServlet</servlet-name>
		<servlet-class>emp.DeleteEmpServlet</servlet-class>
	</servlet>
	<!-- 加载员工 -->
	<servlet>
		<servlet-name>loadEmpServlet</servlet-name>
		<servlet-class>emp.LoadEmpServlet</servlet-class>
	</servlet>
	<!-- 修改员工 -->
	<servlet>
		<servlet-name>modifyEmpServlet</servlet-name>
		<servlet-class>emp.ModifyEmpServlet</servlet-class>		
	</servlet>

	<servlet-mapping>
		<servlet-name>addEmpServlet</servlet-name>
		<url-pattern>/add</url-pattern>
	</servlet-mapping>
	<!-- list -->
	<servlet-mapping>
		<servlet-name>listEmpServlet</servlet-name>
		<url-pattern>/list</url-pattern>
	</servlet-mapping>
	<!-- delete -->
	<servlet-mapping>
		<servlet-name>deleteEmpServlet</servlet-name>
		<url-pattern>/delete</url-pattern>
	</servlet-mapping>
	<!-- load -->
	<servlet-mapping>
		<servlet-name>loadEmpServlet</servlet-name>
		<url-pattern>/load</url-pattern>
	</servlet-mapping>
	<!-- modify -->
	<servlet-mapping>
		<servlet-name>modifyEmpServlet</servlet-name>
		<url-pattern>/modify</url-pattern>
	</servlet-mapping>

</web-app>

3 容器对URI的处理

3.1 问题

请求到达服务器以后,服务器是如何处理URI请求资源路径的,在与web.xml文件中的映射进行比对时的原则是什么

3.2 方案

针对精确匹配、通配符匹配、后缀匹配三种模式修改web.xml文件中的配置,查看不同比对原则的访问结果。

3.3 步骤

步骤一:新建URIServlet.java文件

新建URIServlet.java文件,用于添加简单的输出,标识访问成功。

图 - 9

步骤二:在web.xml文件中添加精确匹配

为URIServlet.java文件添加映射,精确匹配即路径需要严格匹配,如图-10:

图 - 10

步骤三:新建uri.html文件,测试精确匹配

新建uri.html文件,添加两个超链接,地址分别为“abc.html”和“abc/abc.html”。点击不同的连接查看是否能和web.xml文件的映射规则相对应。经过测试后,如果配置模式为“/abc/abc.html”,则路径中应用名称后面的地址需要严格相等,如果只写“abc.html”的话,是不符合匹配原则,无法实现找到URIServlet并执行的。

uri.html文件如下图-11:

图 - 11

步骤四:修改web.xml文件为通配符匹配

修改web.xml中的匹配模式为通配符模式,使用*号代替任意字符,如图-12

图 - 12

步骤五:修改uri.html文件,测试通配符匹配

修改uri.html文件,再次添加两个超链接,一个地址为“abc.html”,由于该通配符前面有abc,则需要先精确匹配abc后再任意长度,所以地址为“abc.html”时,是不能够匹配成功的。第二个地址为“abc/abc/def.html”,第一个abc匹配成功后,不管后面写了任意长度任意字符都代表匹配成功,所以第二个地址可以成功访问到URIServlet。如图-13:

图 - 13

步骤六:修改web.xml文件为后缀匹配

修改web.xml中的匹配模式为后缀匹配模式,使用*.do来代替以do结尾的任意字符,如图-14:

图 - 14

步骤七:修改uri.html文件,测试后缀匹配

修改uri.html文件,再次添加两个超链接,一个地址为“abc.do”,另一个地址为“abc/ abc.do”,不管do前面有多长的字符串统统由*匹配掉了,只要结尾是do就代表匹配成功,所以两个地址都能够访问到URIServlet。如图-15

图 - 15

3.4 完整代码

URIServlet.java文件代码:

package web;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class URIServlet extends HttpServlet{
	
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		System.out.println("这里是URIServlet的service方法");
	}
}

web.xml文件代码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">	
	<!-- URI匹配 -->
	<servlet>
		<servlet-name>uri1</servlet-name>
		<servlet-class>web.URIServlet</servlet-class>
	</servlet>
	<!-- URI匹配 -->
<!--
<servlet-mapping>
		<servlet-name>uri1</servlet-name>
		<url-pattern>/abc/abc.html</url-pattern>
	</servlet-mapping>
<servlet-mapping>
		<servlet-name>uri1</servlet-name>
		<url-pattern>/abc/*</url-pattern>
	</servlet-mapping>
-->
	<servlet-mapping>
		<servlet-name>uri1</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
</web-app>

uri.html文件代码:

<html>
<head>
<body style="font-size:24">
	<h3>请求资源路径的处理</h3>
<ul>
	<li style="margin:40px">精确匹配
		<ul>
			<li>url-pattern: /abc/abc.html
			<li><a href="abc.html">abc.html</a>
			<li><a href="abc/abc.html">abc/abc.html</a>
		</ul>
	<li style="margin:40px">通配符匹配
		<ul>
			<li>url-pattern: /abc/*
			<li><a href="abc.html">abc.html</a>
			<li><a href="abc/abc/def.html">abc/abc/def.html</a>
		</ul>
	<li style="margin:40px">后缀匹配
		<ul>
			<li>url-pattern: *.do
			<li><a href="abc.do">abc.do</a>
			<li><a href="abc/abc.do">abc/abc.do</a>
		</ul>
</ul>
</body>
</html>

4 员工管理——单一控制器实现员工信息管理

4.1 问题

将员工管理的所有操作请求处理合并到一个Servlet中,实现请求的分发。

4.2 方案

使用后缀匹配模式,将以do结尾的请求都提交到ActionServlet中,分析do前的操作请求种类,分发到不同的分支执行相应的动作。同时将JDBC以dao和实体的形式来实现,减少分支内的重复代码。

4.3 步骤

步骤一:新建entity.Employee类

新建与t_emp表对应的实体类,属性与表字段一一对应,添加get/set方法及构造、toString方法。结构如图-16所示

图 - 16

步骤二:新建dao.DBUtil公共类

用于实现数据库的连接和断开的工具类,结构如图-17所示

图 - 17

步骤三:新建dao.EmployeeDAO类

建立针对Employee实体的数据操作类dao。结构如图-18

图 - 18

步骤四:新建emp.ActionServlet类

创建用于进行动作分发的ActionServlet类,通过getRequestURI获取请求资源路径,分析do前面的动作种类,执行不同的分支。

图 -19

步骤五:新建addEmp.html文件

新建用于完成增加员工的表单页面。表单的action地址为ActionServlet对应的add.do,method方式为post

图 - 20

步骤六:修改web.xml文件

使用后缀匹配模式配置ActionServlet的映射原则。

图 - 21

步骤七:部署应用,访问

部署并访问工程,访问地址为/localhost:8080/day03/list.do

4.4 完整代码

entity.Employee.java文件代码:

package entity;

/**
 * 实体类:员工属性与t_emp表中的字段匹配
 */
public class Employee {
	private int id;
	private String name;
	private double salary;
	private int age;

	@Override
	public String toString() {
		return id + "  " + name + "  " + salary + "  " + age;
	}

	public Employee() {
		super();
	}

	public Employee(int id, String name, double salary, int age) {
		super();
		this.id = id;
		this.name = name;
		this.salary = salary;
		this.age = age;
	}

	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 double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}

dao.DBUtil.java文件代码:

package dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import org.apache.taglibs.standard.tag.common.core.OutSupport;

/**
 * JDBC管理连接的工具类,可以获取连接和关闭连接
 */
public class DBUtil {
	/**
	 * 获取连接对象
	 */
	public static Connection getConnection()throws Exception{
		Connection conn = null;
		try {
			Class.forName("oracle.jdbc.OracleDriver");
			conn = DriverManager.getConnection(
					"jdbc:oracle:thin:@localhost:1521:xe", "xxx", "xxx");
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		return conn;
	}
	/**
	 * 关闭连接对象
	 */
	public static void close(Connection conn) throws Exception{
		if(conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
				throw e;
			}
		}
	}
	
	public static void main(String[] args) throws Exception{
		System.out.println(getConnection());
	}
}

dao.EmployeeDAO文件代码:

package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import entity.Employee;

/**
 * Employee的数据操作对象,负责Employee的增删改查
 */
public class EmployeeDAO {
	
	/**
	 * 查询所有员工
	 */
	public List<Employee> findAll() throws Exception{
		List<Employee> emps = new ArrayList<Employee>();
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try{
			conn = DBUtil.getConnection();
			stmt = conn.prepareStatement("select * from t_emp");
			rs = stmt.executeQuery();
			while(rs.next()){
				Employee emp = new Employee(
							rs.getInt("id"),
							rs.getString("name"),
							rs.getDouble("salary"),
							rs.getInt("age")
						);
				emps.add(emp);
			}
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			DBUtil.close(conn);
		}
		return emps;
	}
	
	/**
	 * 删除员工信息
	 */
	public void delete(int id) throws Exception{
		Connection conn = null;
		PreparedStatement stmt = null;
		try{
			conn = DBUtil.getConnection();
			stmt = conn.prepareStatement("delete from t_emp where id=?");
			stmt.setInt(1, id);
			stmt.executeUpdate();
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			DBUtil.close(conn);
		}
	}
	
	/**
	 * 增加员工信息
	 */
	public void save(Employee emp) throws Exception{
		Connection conn = null;
		PreparedStatement stmt = null;
		try{
			conn = DBUtil.getConnection();
			stmt = conn.prepareStatement("insert into t_emp values(emp_id_seq.nextval,?,?,?)");
			stmt.setString(1, emp.getName());
			stmt.setDouble(2, emp.getSalary());
			stmt.setInt(3, emp.getAge());
			stmt.executeUpdate();
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			DBUtil.close(conn);
		}
	}
	
	/**
	 * 根据id查询员工信息
	 */
	public Employee findById(int id) throws Exception{
		Employee emp = null;
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try{
			conn = DBUtil.getConnection();
			stmt = conn.prepareStatement("select * from t_emp where id=?");
			stmt.setInt(1, id);
			rs = stmt.executeQuery();
			if(rs.next()){
				emp = new Employee(
							rs.getInt("id"),
							rs.getString("name"),
							rs.getDouble("salary"),
							rs.getInt("age")
						);
			}
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			DBUtil.close(conn);
		}
		return emp;
	}

	/**
	 * 保存修改员工信息
	 */
	public void modify(Employee emp)throws Exception{
		Connection conn = null;
		PreparedStatement stmt = null;
		try{
			conn = DBUtil.getConnection();
			stmt = conn.prepareStatement("update t_emp set name=?,salary=?,age=? where id=?");
			stmt.setString(1, emp.getName());
			stmt.setDouble(2, emp.getSalary());
			stmt.setInt(3, emp.getAge());
			stmt.setInt(4, emp.getId());
			stmt.executeUpdate();
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			DBUtil.close(conn);
		}
	}
}

emp.ActionServlet文件代码:

package emp;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.EmployeeDAO;
import entity.Employee;

public class ActionServlet extends HttpServlet {
	@Override
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		
		// 获取请求资源路径
		String uri = request.getRequestURI();
		// 获取请求资源路径中除应用名以外的部分
		String action = uri.substring(uri.lastIndexOf("/") + 1,
				uri.lastIndexOf("."));
		
		if (action.equals("list")) {
			try {
				EmployeeDAO dao = new EmployeeDAO();
				List<Employee> emps = dao.findAll();
				out.println("<table>");
				out.println("<caption>员工信息列表</caption>");
				out.println("<tr><td>编号</td><td>姓名</td><td>薪水</td><td>年龄</td><td>操作</td></tr>");
				for (Employee emp : emps) {
					out.println("<tr>");
					out.println("<td>" + emp.getId() + "</td>");
					out.println("<td>" + emp.getName() + "</td>");
					out.println("<td>" + emp.getSalary() + "</td>");
					out.println("<td>" + emp.getAge() + "</td>");
					out.println("<td><a href='#'>删除</a>");
					out.println("<a href='#'>修改</a></td>");
					out.println("</tr>");
				}
				out.println("</table>");
				out.println("<a href='addEmp.html'>增加新员工</a>");
			} catch (Exception e) {
				e.printStackTrace();
				out.print("系统繁忙");
			}
		} else if (action.equals("add")) {
			String name = request.getParameter("name");
			double salary = Double.parseDouble(request.getParameter("salary"));
			int age = Integer.parseInt(request.getParameter("age"));
			try {
				Employee emp = new Employee();
				emp.setName(name);
				emp.setSalary(salary);
				emp.setAge(age);
				EmployeeDAO dao = new EmployeeDAO();
				dao.save(emp);
				response.sendRedirect("list.do");
			} catch (Exception e) {
				e.printStackTrace();
				out.print("系统繁忙");
			}
		}
	}

}

web.xml文件代码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	
	<!-- 合并员工管理多请求-->
	<servlet>
		<servlet-name>actionServlet</servlet-name>
		<servlet-class>emp.ActionServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>actionServlet</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

</web-app>

addEmp.html文件代码:

<html>
<head>
<title>addEmp.html</title>
		<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style="font-size:24px">
<form action="add.do" method="post">
	<fieldset>
		<legend>员工管理</legend>
		姓名:<input name="name"/><br>
		薪水:<input name="salary"/><br>
		年龄:<input name="age"/><Br>
		<input type="submit" value="增加"/>
	</fieldset>
</form>
</body>
</html>

5 Servlet的生命周期

5.1 问题

Servlet生命周期的四个阶段的执行时机。

5.2 方案

创建一个LifeServlet类,添加构造方法、init方法的重写、destroy方法的重写及service方法,通过部署动作、访问动作、停止服务器动作观察方法的执行情况及执行次数。

5.3 步骤

步骤一:创建LifeServlet类,实现四个阶段对应的四个方法

图 - 22

步骤二:修改web.xml完成配置

图 - 23

步骤三:访问Servlet查看输出

部署应用,启动服务,此时输出中没有四个方法的任何输出,在地址栏中输入url后,在控制台输出如图-24内容。构造和初始化及service都被指定,但构造和初始化只会执行这一次,再次刷新页面,会发现只有service方法被执行。如图-25所示。

图– 24

图 - 25

步骤四:修改web.xml文件,添加<load-on-startup>配置

添加配置说明,使得Servlet的实例化初始化发生在服务器启动时或部署时。

图 - 26

步骤五:重新部署,不访问查看输出

重新部署程序,但不在地址栏中访问应用,会发现实例化和初始化阶段已经被执行了。

图 - 27

步骤六:停止服务查看输出

停止服务器,第4个阶段销毁阶段执行。如图-28:

图 - 28

步骤七:修改web.xml文件,添加<init-param>配置

增加init-param配置,即增加servlet的初始参数,可以在初始化阶段读取该值。

图 - 29

步骤八:修改service方法获取init-param参数并输出

初始参数的获取,可以使用ServletConfig对象的getInitParameter方法,而ServletConfig对象使用getServletConfig()方法获取。

图 - 30

步骤九:访问service,查看参数的输出情况

图 - 31

5.4 完整代码

LifeServlet.java文件代码:
	package web;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LifeServlet extends HttpServlet {	
	/**
	 * 阶段1:实例化
	 */
	public LifeServlet() {
		System.out.println("1.Constructor is running ...");
	}
	/**
	 * 阶段2:初始化
	 */
	@Override
	public void init() throws ServletException {
		System.out.println("2.Init is running ...");
	}	
	/**
	 * 阶段3:就绪
	 */
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		//读取初始化参数
		ServletConfig config = getServletConfig();
		String name = config.getInitParameter("company");
		String address = config.getInitParameter("address");
		System.out.println("Service is running ..." );
		System.out.println("初始参数为:" + name+"   " + address);		
	}
	
	/**
	 * 阶段4:销毁
	 */
	@Override
	public void destroy() {
		super.destroy();
		System.out.println("4.Destroy is running...");
	}

}

web.xml文件代码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<!-- 生命周期 -->
	<servlet>
		<servlet-name>lifeServlet</servlet-name>
		<servlet-class>web.LifeServlet</servlet-class>		
		<init-param>
			<param-name>company</param-name>
			<param-value>Tarena</param-value>
		</init-param>
		<init-param>
			<param-name>address</param-name>
			<param-value>Beijing</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<!-- 生命周期 -->
	<servlet-mapping>
		<servlet-name>lifeServlet</servlet-name>
		<url-pattern>/life</url-pattern>
	</servlet-mapping>
</web-app>

6 ServletContext对象(Servlet上下文对象)

6.1 问题

实现访问网站总人数的记录,以及基于某一特定起点的访问记录。

6.2 方案

由于网站中的资源较多,要想保留每一次的访问计数则需要一个从应用一启动就存在的空间,并且应用中的所有资源都能访问到这个存储空间,所以使用ServletContext及Servlet上下文对象保存每一次的访问计数。当计数需要基于某一个特定的数字开始时,可以使用上下文配置参数,存储在web.xml文件中,当应用启动时就可以读取到这个初始值,再实现计数。

6.3 步骤

步骤一:新建Context01Servlet.java和Context02Servlet.java文件

两个文件内容一致,获取上下文对象后判断是否是第一次访问,是第一次则初始值为1,不是第一次就将上次的值增1,如图-32所示:

图 - 32

步骤二:配置web.xml文件

为两个ContextServlet添加配置:

图 - 33

步骤三:访问不同的Servlet查看计数效果

访问两个Servlet,刷新后计数递增,并且递增后在另一个servlet中访问会基于这个递增后的数字,如图-34:

图 - 34

步骤四:修改web.xml文件,增加context-param配置

为该应用增加上下文初始参数如图-35所示。一个count-1000的键值对。通过上下文对象的getInitParameter方法可以获取该值。

图 - 35

步骤五:修改Context01和Context02,使计数从设定的初值开始

修改两个文件的代码,从初始参数开始计数,如图-36:

图 - 36

步骤六:访问不同的Servlet查看计数效果

重新部署后,交替访问两个ContextServlet,可以看到计数从初始参数开始。

图 - 37

6.4 完整代码

Context01Servlet.java和Context02Servlet.java文件代码如下:

package web;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;



public class Context01Servlet extends HttpServlet{
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		//获取全局的上下文对象
		ServletContext context = getServletContext();		
		Object count = context.getAttribute("count");
		if(count==null){
			context.setAttribute("count", context.getInitParameter("count"));
			//context.setAttribute("count", 1);
		}else{
			context.setAttribute("count", Integer.parseInt(count.toString())+1);
		}
		out.print("总浏览量为:"+context.getAttribute("count"));
	}
}

web.xml文件代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<!-- Servlet上下文 -->
	<context-param>
		<param-name>count</param-name>
		<param-value>1000</param-value>
	</context-param>
	<servlet>
		<servlet-name>context01Servlet</servlet-name>
		<servlet-class>web.Context01Servlet</servlet-class>		
	</servlet>
	<servlet>
		<servlet-name>context02Servlet</servlet-name>
		<servlet-class>web.Context02Servlet</servlet-class>		
	</servlet>

	<servlet-mapping>
		<servlet-name>context01Servlet</servlet-name>
		<url-pattern>/context01</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>context02Servlet</servlet-name>
		<url-pattern>/context02</url-pattern>
	</servlet-mapping>
</web-app>

7 Servlet线程安全

7.1 问题

每个Servlet只会创建一个对象实例,多个请求同时访问这个实例时,如果有修改属性的操作,那么就会有线程安全的隐患,也就是说Servlet是线程不安全的。如何避免线程不安全带来的后果?

7.2 方案

使用Apache组织开发Apache Jmeter进行压力测试,模拟5个请求,观察线程在同时修改私有属性时产生的后果。修改Servlet中的代码,增加同步的功能以避免线程不安全带来的后果。

7.3 步骤

步骤一:新建ThreadSafeServlet类

在这个Servlet中增加一个私有属性,service方法中修改属性。模拟网络延迟,线程休眠1秒后再输出这个属性。如图-38:

图 - 38

步骤二:修改web.xml配置映射

图 -39

步骤三:使用Jmeter模拟5个HTTP请求

下载Jmeter之后,解压缩,使用命令行工具进入到解压后的bin目录下,输入“jmeter”代表启动Jmeter,然后新建测试计划(线程组。如图-40所示:

图– 40

图– 41

改写完线程数为5以后,在线程组上右键,新建HTTP请求,配置请求参数成功后,即可启动。

图– 42

图- 43

步骤四:查看控制台输出结果

图– 44

从结果中可以看到五个线程产生了修改值的冲突问题,也就是线程不安全现象。

步骤五:修改ThreadSafeServlet,增加同步功能

图 - 45

步骤六:运行Jmeter再次发送5个请求

步骤七:查看控制台输出结果

图– 46

根据图-46可以看到,使用同步后保证了线程安全,不会产生访问冲突的现象。

7.4 完整代码

ThreadSafeServlet.java文件代码:

package web;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ThreadSafeServlet extends HttpServlet {

	/** 私有属性count*/
	private int count = 0;

	@Override
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		synchronized (this) {
			count++;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + ":" + count);
		}

	}
}

web.xml文件代码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	
	<!-- 线程安全 -->
	<servlet>
		<servlet-name>threadSafeServlet</servlet-name>
		<servlet-class>web.ThreadSafeServlet</servlet-class>		
	</servlet>
	<servlet-mapping>
		<servlet-name>threadSafeServlet</servlet-name>
		<url-pattern>/safe</url-pattern>
	</servlet-mapping>
</web-app>