Servlet 基础
JSP 的前身就是 Servlet
什么是 Servlet?
Servlet 是在服务器上运行的小程序。一个 Servlet 就是一个 Java 类,并且可以通过“请求 - 响应”编程模型来访问这个驻留在服务器内存里的 Servlet 程序。
Tomcat 容器等级
Tomcat 容器分为四个等级,Servlet 容器管理 Context 容器,一个 Context 对应一个 Web 工程
具体如上图所示: Tomcat 容器 : Container 容器 --> Engine 容器(引擎容器) --> HOST (主机)容器 --> Servlet --> Context
手工编写第一个 Servlet
1、新建一个 MyFirstServletDemo 动态项目,并新建一个 index.jsp 文件
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<a href="servlet/HelloServlet">Get请求HelloServlet</a>
<form action="servlet/HelloServlet" method="post">
<input type="submit" value="Post请求HelloServlet"/>
</form>
</body>
</html>
2、在src中新建一个servlet包,再定义一个HelloServlet类,并重写 doGet 和 doPost 方法
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 HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub System.out.println("处理Get请求……"); PrintWriter out = resp.getWriter(); resp.setContentType("text/html; charset=utf-8"); out.println("<h1>Hello Servlet</h1>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub System.out.println("处理Post请求……"); PrintWriter out = resp.getWriter(); resp.setContentType("text/html; charset=utf-8"); out.println("<h1>Hello Servlet</h1>"); } }
3、在 WEB-INF - web.xml 中注册servlet,添加 servlet 和 servlet-mapping 两个标签
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>MyFirstServletDemo</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <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> </web-app>
运行查看
从这个例子中,可以大概看出 Servlet 的执行流程
Servlet 的生命周期
Servlet 生命周期阶段包括:初始化、加载、实例化、服务和销毁
1、初始化阶段,调用 init() 方法;
2、响应客户请求阶段,调用 service() 方法。由 service() 方法根据提交方式选择执行 doGet() 或者 doPost() 方法
3、终止阶段,调用 destroy() 方法
下面用代码演示下 Servlet的生命周期
1、新建一个 testServlet.jsp 页面
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<h1>测试 Servlet 生命周期</h1>
<a href="servlet/TestServlet1">发起Get请求</a>
</body>
</html>
2、新建一个TestServlet1 类,并重写父类的构造方法,初始化方法,doGet方法和销毁方法
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的构造方法被执行……"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub System.out.println("处理Get请求……"); resp.setContentType("text/html; charset=utf-8"); PrintWriter out = resp.getWriter(); out.println("<h1>Test Servlet1<h1>"); } @Override public void destroy() { // TODO Auto-generated method stub System.out.println("TestServlet1的销毁方法被执行……"); } @Override public void init() throws ServletException { // TODO Auto-generated method stub System.out.println("TestServlet1的初始化方法被执行……"); } }
3、修改配置文件
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>MyFirstServletDemo</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>TestServlet1</servlet-name> <servlet-class>servlet.TestServlet1</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet1</servlet-name> <url-pattern>/servlet/TestServlet1</url-pattern> </servlet-mapping> </web-app>
从下图中可以看到依次执行的是 构造方法、初始化方法和service方法,当servlet实例已经存在的时候直接调用service方法,而不需要再去执行构造方法和初始化方法。只有当服务器关闭的时候,才会执行销毁方法
Servlet 容易装载 Servlet的三种情况:
1、Servlet 容器启动时自动装载某些Servlet,实现它只需要在 web.xml 文件中的 <Servlet></Servlet> 之间添加代码:<load-on-start-up>1</load-on-start-up> 数字越小,表示优先级越高
2、在Servlet 容器启动后,客户首次向 Servlet 发送请求
3、Serlvet 类文件被更新后,重新装载 Servlet
其实,上例演示的也就是第2种情况,启动Servlet容器,首次向 Servlet发送请求
现在我们来演示第1种情况,在 servlet包下创建一个 TestServlet2 类
package servlet; import java.io.IOException; import java.io.PrintWriter; 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 TestServlet2 extends HttpServlet { public TestServlet2() { System.out.println("TestServlet2的构造方法被执行……"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub System.out.println("处理Get请求……"); resp.setContentType("text/html; charset=utf-8"); PrintWriter out = resp.getWriter(); out.println("<h1>Test Servlet2<h1>"); } @Override public void destroy() { // TODO Auto-generated method stub System.out.println("TestServlet2的销毁方法被执行……"); } @Override public void init() throws ServletException { // TODO Auto-generated method stub System.out.println("TestServlet2的初始化方法被执行……"); } }
2、修改web.xml 配置文件,在 servlet 标签中加一对 load-on-startup,把TestServlet1的优先级设为2,低于 TestServlet1的优先级1
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>MyFirstServletDemo</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>TestServlet1</servlet-name> <servlet-class>servlet.TestServlet1</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>TestServlet1</servlet-name> <url-pattern>/servlet/TestServlet1</url-pattern> </servlet-mapping> <servlet> <servlet-name>TestServlet2</servlet-name> <servlet-class>servlet.TestServlet2</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>TestServlet2</servlet-name> <url-pattern>/servlet/TestServlet2</url-pattern> </servlet-mapping> </web-app>
重新启动Tomcat服务器,我们发现服务器先执行的是TestServlet2的构造方法和初始化方法
现在来实现一个小例子,用户注册完成以后,把用户的注册信息在另一个页面打印出来
1、reg.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>My JSP 'reg.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> <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="mypassword" ></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="gender" checked="checked" value="Male">男<input type="radio" name="gender" 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>
2、web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>getServletUserInfo2</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>RegServlet</servlet-name> <servlet-class>servlet.RegServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>RegServlet</servlet-name> <url-pattern>/servlet/RegServlet</url-pattern> </servlet-mapping> </web-app>
3、JavaBean Users.java
package user; import java.util.Date; /** * 用户类 * @author wupeng * */ public class Users { private String username; // 用户名 private String mypassword; // 密码 private String gender; // 性别 private String email; // 邮箱 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 getMypassword() { return mypassword; } public void setMypassword(String mypassword) { this.mypassword = mypassword; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } 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; } }
4、RegServlet.java
package servlet; import java.io.IOException; 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 user.Users; public class RegServlet extends HttpServlet { public RegServlet() { // TODO Auto-generated constructor stub } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub super.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub req.setCharacterEncoding("utf-8"); Users u = new Users(); String username, mypassword, gender, email, introduce, isAccept; Date birthday; String[] favorites; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { username = req.getParameter("username"); mypassword = req.getParameter("mypassword"); gender = req.getParameter("gender"); email = req.getParameter("email"); introduce = req.getParameter("introduce"); birthday = sdf.parse(req.getParameter("birthday")); if(req.getParameterValues("isAccpet")!=null) { isAccept = req.getParameter("isAccept"); } else { isAccept = "false"; } favorites = req.getParameterValues("favorite"); // 把用户信息存到对象u中 u.setUsername(username); u.setMypassword(mypassword); u.setGender(gender); u.setEmail(email); u.setIntroduce(introduce); u.setBirthday(birthday); if ("true".equals(isAccept)) { u.setFlag(true); } else { u.setFlag(false); } u.setFavorites(favorites); // 把注册成功的用户信息保存到 session 中 req.getSession().setAttribute("regUser", u); // 服务器转发到注册成功页面 req.getRequestDispatcher("../userinfo.jsp").forward(req, resp); } catch (Exception e) { e.printStackTrace(); } } }
5、userinfo.jsp
<%@ page language="java" import="java.util.*,java.text.*" 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>My JSP 'userinfo.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> <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="user.Users" scope="session" /> <table width="600" cellpadding="0" cellspacing="0" border="1"> <tr> <td class="title">用户名:</td> <td class="content"> <jsp:getProperty property="username" name="regUser"/></td> </tr> <tr> <td class="title">密码:</td> <td class="content"> <jsp:getProperty property="mypassword" name="regUser"/></td> </tr> <tr> <td class="title">性别:</td> <td class="content"> <jsp:getProperty property="gender" name="regUser"/></td> </tr> <tr> <td class="title">E-mail:</td> <td class="content"> <jsp:getProperty property="email" name="regUser"/></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(); for (String f:favorites) { %> <%=f %>" " <% } %> </td> </tr> <tr> <td class="title">自我介绍:</td> <td class="content"> <jsp:getProperty property="introduce" name="regUser"/></td> </tr> <tr> <td class="title">是否介绍协议:</td> <td class="content"> <jsp:getProperty property="flag" name="regUser"/></td> </tr> </table> </center> </body> </html>
页面输出:
Servlet 路径跳转
1、index.jsp 中的路径跳转
<a href="request.jsp?username=科比">测试URL传参数</a> <!-- 使用相对路径访问 Servlet --> <!-- /servlet/HelloServelt 第一个/ 表示服务器的根目录 --> <a href="servlet/HelloServlet">访问HelloServlet</a> <!-- 使用绝对路径访问Servlet,可以使用 path 变量:path变量表示项目的根目录 --> <a href="<%=path %>/servlet/HelloServlet">访问HelloServlet</a> <!-- 表单中 action的 URL地址写法,与超链接方式完全相同 -->
2、web.xml 中 url-pattern
<servlet-mapping> <servlet-name>HelloServlet</servlet-name> <!-- url-pattern 处必须以 / 开头,这里的 / 表示项目的根目录 --> <url-pattern>/servlet/HelloServlet</url-pattern> </servlet-mapping>
3、servlet 跳转
// 请求重定向方式跳转到 test.jsp,当前路径是 ServletPathDirection/servlet // response.sendRedirect("test.jsp"); // 使用 request.getContextPath 获得上下文对象 // response.sendRedirect(request.getContextPath + "/test.jsp"); // 服务器内部跳转,这里的斜线表示项目的根目录 request.getRequestDispatcher("/test.jsp").forward(request, response);