Servlet 介绍
Posted 白桂任的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet 介绍相关的知识,希望对你有一定的参考价值。
JSP 的本质就是 Servlet,开发者把编写好的 JSP 页面部署在 Web 容器中后,Web 容器会将 JSP 编译成对应的 Servlet。
Servlet 的开发
Servlet 是个特殊的 Java 类,这个 Java 类必须继承 HttpServlet。每个 Servlet 可以响应客户端的请求。Servlet 提供不同的方法用于响应客户端请求。
doGet:用于响应客户端的 GET 请求。
doPost:用于响应客户端的 POST 请求。
doPut:用于响应客户端的 PUT 请求。
doDelete:用于响应客户端的 DELETE 请求。
事实上,客户端的请求通常只有 GET 和 POST 两种,Servlet 为了响应者两种请求,必须重写 doGet() 和 doPost() 两个方法。如果 Servlet 为了响应 4 种方式的请求,则需要同时重写上面的 4 个方法。
大部分时候,Servlet 对于所有请求的响应都是完全一样的。此时,可以采用重写一个方法来代替上面的几个方法:只需要重写 service() 方法即可响应客户端的所有请求。
另外,HttpServlet 还包含两个方法。
init(ServletConfig config):创建 Servlet 实例时,调用该方法来初始化 Servlet 资源。
destroy():销毁 Servlet 实例时,自动调用该方法回收资源。
通常无需重写 init() 和 destroy() 方法,除非需要在初始化 Servlet 时,完成某些资源初始化的方法,才考虑重写 init 方法。如果需要在销毁 Servlet 之前,先完成某些资源的回收,比如关闭数据库连接等,才需要重写 destroy 方法。
package com.baiguiren; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.annotation.*; // Servlet 必须继承 HttpServlet 类 @WebServlet(name="firstServlet", urlPattern={"/firstServlet"}) public class FirstServlet extends HttpServlet { // 客户端的响应方法,使用该方法可以响应客户端所有类型的请求 public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置解码方式 request.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); // 获取 name 的请求参数值 String name = request.getParameter("name"); // 获取 gender 的请求参数值 String gender = request.getParameter("gender"); // 获取 color 的请求参数值 String[] colors = request.getParameterValues("color"); // 获取 country 的请求参数值 String country = request.getParameter("country"); // 获取页面输出流 PrintStream out = new PrintStream(response.getOutputStream()); // 输出 HTML 页面标签 out.println("<html>"); out.println("<head>"); out.println("<title>Servlet测试</title>"); out.println("</head>"); out.println("<body>"); // 输出请求参数的值:name out.println("你的名字:" + name + "<br/>"); // 输出请求参数的值:gender out.println("你的性别:" + gender + "<br/>"); // 输出请求参数的值:color out.println("你喜欢的颜色:"); for (String color : colors) { out.println(color + " "); } out.println("<br/>"); // 输出请求参数的值:country out.println("你来自的国家:" + country + "<hr/>"); out.println("</body>"); out.println("</html>"); } }
Servlet 的配置
编辑好的 Servlet 源文件并不能直接响应用户请求,还必须将其编译成 class 文件。将编译后的 FirstServlet.class 放在 WEB-INF/classes 路径下,如果 Servlet 有包,则还应将 class 文件放在对应的包路径下。例如,上面的 package 为 com.baiguiren,那么 FirstServlet.class 的最终路径应该是 WEB-INF/classes/com/baiguiren/FirstServlet.class。
如果需要直接采用 javac 命令来编译 Servlet 类,则需要将 Servlet API 接口和类添加到系统的 CLASSPATH 环境变量里。也就是将 tomcat 安装目录的 lib 子目录下的 servlet-api.jar 和 jsp-api.jar 添加到 CLASSPATH 环境变量中。
为了让 Servlet 能响应用户请求,还必须将 Servlet 配置在 Web 应用中(也就是配置在 web.xml 中)。
从 Servlet3.0 开始,配置 Servlet 有两种方式:
1、在 Servlet 类中使用 @Servlet 注解进行配置
2、通过在 web.xml 文件中进行配置
使用 @Servlet 时可指定如下表属性
属性 | 说明 |
asyncSupported | 指定该 Servlet 是否支持异步操作模式 |
displayName | 指定该 Servlet 的显示名 |
initParams | 用于为该 Servlet 配置参数 |
loadOnStartup | 用于将该 Servlet 配置成 load-on-startup 的 Servlet |
name | 指定该 Servlet 的名称 |
urlPatterns/value | 这两个属性的作用完全相同,都指定该 Servlet 处理的 URL |
如果打算用注解来配置 Servlet,有点需要指出
a、不要在 web.xml 文件的根元素 (<web-app.../>) 中指定 metadata-complete="true"
b、不要在 web.xml 文件中配置该 Servlet。
如果打算使用 web.xml 文件来配置该 Servlet,则需要配置如下两个部分。
a、配置 Servlet 的名字:对应 web.xml 文件中的 <servlet/> 元素。
b、配置 Servlet 的 URL:对应 web.xml 文件中的 <servlet-mapping/> 元素。这一步是可选的。但如果没有为 Servlet 配置 URL,则该 Servlet 不能响应用户请求。
使用 web.xml 配置 Servlet 示例:
<!-- 配置 Servlet 的名字 --> <servlet> <!-- 指定 Servlet 的名字,相当于指定 @WebServlet 的 name 属性 --> <servlet-name>firstServlet</servlet-name> <!-- 指定 Servlet 的实现类 --> <servlet-class>com.baiguiren.FirstServlet</servlet-class> </servlet> <!-- 配置 Servlet 的 URL --> <servlet-mapping> <!-- 指定 Servlet 的名字 --> <servlet-name>firstServlet</servlet-name> <!-- 指定 Servlet 映射的 URL 地址,相当于指定 @WebServlet 的 urlPatterns 属性 --> <url-pattern>/aa</url-pattern> </servlet-mapping>
如果在 web.xml 文件中增加了如上所示的配置片段,则该 Servlet 的 URL 为 /aa。如果没有在 web.xml 中增加上面的片段,那么该 Servlet 类上的 @WebServlet 注解就会起作用,该 Servlet 的 URL 为 /firstServlet。
JSP/Servlet 的生命周期
JSP 的本质就是 Servlet,开发者编写的 JSP 页面将由 Web 容器编译成对应的 Servlet,当 Servlet 在容器中运行时,其示例的创建及销毁等都不是由程序员决定的。而是由 web 容器进行控制的。
创建 Servlet 示例有两个时机。
1、客户端第一次请求某个 Servlet 时,系统创建该 Servlet 的实例:大部分的 Servlet 都是这种 Servlet
2、web 应用启动时立即创建 Servlet 实例,即 load-on-startup Servlet。
每个 Servlet 的运行都遵循如下生命周期:
1、创建 Servlet 实例
2、web 容器调用 Servlet 的 init 方法,对 Servlet 进行初始化。
3、Servlet 初始化后,将一直存在于容器中,用于响应客户端请求。如果客户端发送 GET 请求,容器调用 Servlet 的 doGet 方法处理并响应请求;如果客户端发送 POST 请求,容器调用 Servlet 的 doPost 方法处理并响应请求。或者统一使用 service() 方法处理来响应用户请求。
4、web 容器决定销毁 Servlet 时,先调用 Servlet 的 destroy 方法,通常在关闭 web 应用之时销毁 Servlet。
Servlet 的生命周期如下图所示:
load-on-startup Servlet
应用启动时就创建的 Servlet,通常是用于某些后台服务的 Servlet,或者需要拦截很多请求的 Servlet:这种 Servlet 通常作为应用的基础 Servlet 使用,提供重要的后台服务。
配置 load-on-startup 的 Servlet 有两种方式。
1、在 web.xml 文件中通过 <servlet.../> 元素的 <load-on-startup.../> 子元素进行配置
2、通过 @WebServlet 注解的 loadOnStartup 属性指定。
<load-on-startup.../> 元素或 loadOnStartup 属性都只接收一个整型值,这个值越小,Servlet 就越优先实例化。
package com.baiguiren; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.annotation.*; // Servlet 必须继承 HttpServlet 类 @WebServlet(loadOnStartup=1) public class TimerServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); Timer timer = new Timer(); timer.schedule(new TimerTask(){ @Override public void run() { System.out.println(new Date()); } }, 1000); } }
上面是一个简单的 Servlet,该 Servlet 不响应用户请求,它仅仅执行计时器功能,每隔一段时间在控制台打印出当前时间。
这个 Servlet 没提供 service() 方法,这表明它不能响应用户请求,所以无须为它配置 URL 映射。
上面的 Servlet 使用了注解配置了 load-on-startup Servlet,除此之外,还可以在 web.xml 文件中增加如下配置片段。
<servlet> <!-- Servlet 名 --> <servlet-name>timerServlet</servlet-name> <!-- Servlet 实现类 --> <servlet-class>com.baiguiren.TimerServlet</servlet-class> <!-- 配置应用启动时,创建 Servlet 实例,相当于指定 @WebServlet 的 loadOnStartup 属性 --> <load-on-startup>1</load-on-startup> </servlet>
访问 Servlet 的配置参数
配置 Servlet 时,还可以增加额外的配置参数。通过使用配置参数,可以实现提供更好的可移植性。
为 Servlet 配置参数有两种方式:
1、通过 @WebServlet 的 initParams 属性来指定。
2、通过在 web.xml 文件的 <servlet.../> 元素中添加 <init-param.../> 子元素来指定。
第二种方式与为 JSP 配置初始化参数极其相似,因为 JSP 的实质就是 Servlet,而且配置 JSP 的实质就是把 JSP 当 Servlet 使用。
访问 Servlet 配置参数通过 ServletConfig 对象完成,ServletConfig 提供如下方法:
String getInitParameter(String name):用于获取初始化参数。
JSP 的内置对象 config 就是此处的 ServletConfig。
TestServlet.java
package com.baiguiren; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.sql.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.annotation.*; // Servlet 必须继承 HttpServlet 类 @WebServlet( name="testServlet", urlPatterns={"/testServlet"}, initParams={ @WebInitParam(name="driver", value="com.mysql.jdbc.Driver"), @WebInitParam(name="url", value="jdbc:mysql://localhost:3306/jsp"), @WebInitParam(name="user", value="root"), @WebInitParam(name="pass", value="root") } ) public class TestServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); } // 响应客户端请求的方法 public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // 获取 ServletConfig 对象 ServletConfig config = getServletConfig(); // 通过 ServletConfig 对象获取配置参数:driver String driver = config.getInitParameter("driver"); // 通过 ServletConfig 对象获取配置参数:url String url = config.getInitParameter("url"); // 通过 ServletConfig 对象获取配置参数:user String user = config.getInitParameter("user"); // 通过 ServletConfig 对象获取配置参数:pass String pass = config.getInitParameter("pass"); // 注册驱动 Class.forName(driver); // 获取数据库连接 Connection conn = DriverManager.getConnection(url, user, pass); // 创建 Statement 对象 Statement stmt = conn.createStatement(); // 执行查询,获取 ResultSet 对象 ResultSet rs = stmt.executeQuery("select * from person"); // 设置响应类型 response.setContentType("text/html; charset=UTF-8"); // 获取页面输出流 PrintStream out = new PrintStream(response.getOutputStream()); // 输出 HTML 标签 out.println("<html>"); out.println("<head>"); out.println("<title>访问 Servlet 初始化参数测试</title>"); out.println("</head>"); out.println("<body>"); out.println("<table border=\'1\' width=\'480\'>"); // 遍历结果集 while(rs.next()) { // 输出结果集内容 out.println("<tr>"); out.println("<td>" + rs.getString(1) + "</td>"); out.println("<td>" + rs.getString(2) + "</td>"); out.println("</tr>"); } out.println("</table>"); out.println("</body></html>"); } catch (Exception e) { e.printStackTrace(); } } }
ServletConfig 获取配置参数的方法和 ServletContext 获取配置参数的方法完全一样,只是 ServletConfig 是取得当前 Servlet 的配置参数,而 ServletContext 是获取整个 Web 应用的配置参数。
以上程序中 @WebServlet 中的 initParams 属性用于为该 Servlet 配置参数,initParams 属性值的每个 @WebInitParam 配置一个初始化参数,每个 @WebInitParam 可指定如下两个属性:
a、name:指定参数名
b、value:指定参数值
类似地,在 web.xml 文件中为 Servlet 配置参数使用 <init-param.../> 元素,该元素可以接受如下两个子元素。
a、param-name:指定配置参数名
b、param-value:指定配置参数值
<servlet> <!-- 配置 Servlet 名 --> <servlet-name>testServlet</servlet-name> <!-- 指定 Servlet 的实现类 --> <servlet-class>com.baiguire.TestServlet</servlet-class> <!-- 配置 Servlet 的初始化参数:driver --> <init-param> <param-name>driver</param-name> <param-value>com.mysql.jdbc.Driver</param-value> </init-param> <!-- 配置 Servlet 的初始化参数:url --> <init-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/jsp</param-value> </init-param> <!-- 配置 Servlet 的初始化参数:user --> <init-param> <param-name>user</param-name> <param-value>root</param-value> </init-param> <!-- 配置 Servlet 的初始化参数:pass --> <init-param> <param-name>pass</param-name> <param-value>root</param-value> </init-param> </servlet> <servlet-mapping> <!-- 确定 Servlet 名 --> <servlet-name>testServlet</servlet-name> <!-- 配置 Servlet 映射的 URL --> <url-pattern>/testServlet</url-pattern> </servlet-mapping>
使用 Servlet 作为控制器
在标准的 MVC 模式中,Servlet 仅作为控制器使用。JavaEE 应用架构正是遵循 MVC 模式的,对于遵循 MVC 模式的 JavaEE 应用而言,JSP 仅作为表现层 (View) 技术,其作用有两点、
1、负责收集用户请求参数
2、将应用的处理结果、状态数据呈现给用户
Servlet 则仅充当控制器 (Controller) 角色,它的作用类似于调度员,所有用户请求都发给 Servlet,Servlet 调用 Model 来处理用户请求,并调用 JSP 来呈现处理结果;或者 Servlet 直接用 JSP 将应用的状态数据呈现给用户。
Model 通常由 JavaBean 来充当,所有业务逻辑、数据访问逻辑都在 Model 中实现。实际上隐藏在 Model 下的可能还有丰富的组件,例如 DAO 组件、领域对象等。
login.jsp
<%@ page contentType="text/html; charset=UTF-8" %> <html> <head> <title>login</title> </head> <body> <!-- 输出错误提示 --> <span style="color:red; font-weight: bold;"> <% if (request.getAttribute("err") != null) { out.println(request.getAttribute("err") + "</br>"); } %> </span> 请输入用户名和密码: <!-- 登录表单,该表单提交到一个 Servlet --> <form method="POST" action="login"> 用户名:<input type="text" name="username"/><br/> 密码:<input type="password" name="password"/><br/> <input type="submit" value="登录"/><br/> </form> </body> </html>
LoginServlet.java
package com.baiguiren; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.sql.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.annotation.*; // Servlet 必须继承 HttpServlet 类 @WebServlet( name="login", urlPatterns={"/login"} ) public class LoginServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String errMsg = "error: "; // Servlet 本身并不输出响应到客户端,因此必须将请求转发到视图页面 RequestDispatcher rd; // 获取请求参数 String username = request.getParameter("username"); String password = request.getParameter("password"); try { // Servlet 本身并不执行任何的业务逻辑处理,它调用 JavaBean 处理用户请求 DbDao dao = new DbDao("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/jsp", "root", "root"); // 查询结果集 ResultSet rs = dao.query("select password from user where username = ?", username); if (rs.next()) { // 用户名和密码匹配 if (rs.getString("password").equals(password)) { // 获取 session 对象 HttpSession session = request.getSession(true); // 设置 session 属性,跟踪用户会话状态 session.setAttribute("username", username); // 获取转发对象 rd = request.getRequestDispatcher("/welcome.jsp"); // 转发请求 rd.forward(request, response); } else { // 用户名和密码不匹配 errMsg += "密码不正确"; } } else { // 用户名不存在 errMsg += "用户名不存在"; } } catch (Exception e) { e.printStackTrace(); } // 如果出错,转发到重新登录 if (errMsg != null && !(errMsg.equals(""))) { rd = request.getRequestDispatcher("/login.jsp"); request.setAttribute("err", errMsg); rd.forward(request, response); } } }
DbDao.java
package com.baiguiren; import java.sql.*; public class DbDao { private Connection connection; private String driver; private String url; private String user; private String pass; public DbDao() {} public DbDao(String driver, String url, String user, String pass) { this.driver = driver; this.url = url; this.user = user; this.pass = pass; } // 下面是各成员属性的 setter 和 getter 方法 public void setDriver(String driver) { this.driver = driver; } public String getDriver() { return this.driver; } public void setUrl(String url) { this.url = url; } public String getUrl() { return this.url; } public void setUser(String user) { this.user = user; } public String getUser() { return this.user; } public void setPass(String pass) { this.pass = pass; } // 获取数据库连接 public Connection getConnection() throws Exception { if (connection == null) { Class.forName(this.driver); connection = DriverManager.getConnection(url, user, pass); } return connection; } // 插入记录 public boolean insert(String sql, Object... args) throws Exception { PreparedStatement pstmt = getConnection().prepareStatement(sql); for (int i = 0; i < args.length; i++) { pstmt.setObject(i + 1, args[i]); } return pstmt.executeUpdate() == 1; } // 执行查询 public ResultSet query(String sql, Object... args) throws Exception { PreparedStatement pstmt = getConnection().prepareStatement(sql); for (int i = 0; i < args.length; i ++) { pstmt.setObject(i + 1, args[i]); } return pstmt.executeQuery(); } // 执行修改 public void modify(String sql, Object... args) throws Exception { PreparedStatement pstmt = getConnection().prepareStatement(sql); for (int i = 0; i < args.length; i++) { pstmt.setObject(i + 1, args[i]); } pstmt.executeUpdate(); pstmt.close(); } // 关闭数据库连接的方法 public void closeConnection() throws Exception { if (connection != null && !connection.isClosed()) { connection.close(); } } }
下面是 MVC 中各个角色的对应组件
a、M:Model,即模型,对应 JavaBean
b、V:View,即视图,对应 JSP 页面
c、C:Controller,即控制器,对应 Servlet
以上是关于Servlet 介绍的主要内容,如果未能解决你的问题,请参考以下文章
Android课程---Android Studio使用小技巧:提取方法代码片段