Servlet是使用Java Servlet应用程序编程接口及相关类和方法所构成的java程序(没错,Servlet本质上就是运行在服务器环境下的java程序),它在服务器端的servlet容器(支持servlet容器的有Apache基金会的Tomcat服务器)中运行。
Servlet程序与传统的从命令行编译的java程序的不同点在于,Servlet是由J2EE中的Servlet容器加载出来的,它是不能直接在命令行中执行的。
Servlet能处理HTTP请求,然后返回一系列处理后的结果,并可以动态地生成新的web页面。
在JSP页面文件中应该仅仅包含与web表现层有关的标签元素,而所有的数据计算,数据分析,数据库连接等功能处理和业务逻辑的实现代码,都应该放在JavaBean组件或Servlet程序中。
因为尽管.jsp文件是可以用java代码描述html页面的文件,但还是不应该在jsp页面中加入太多的java代码逻辑。
<%@ page language="java" contentType="text/html; charset=gbk"%>
<%@ page errorPage="execp.jsp"%>
<%
String strprice=request.getParameter("price");
double price=Double.parseDouble(strprice);
out.println("Total price="+price*3);
%>
<%@ page language="java" contentType="text/html; charset=gbk"%>
<%@ page isErrorPage="true"%>
<%
out.println("exception.toString:");
out.println("<br>");
out.println(exception.toString());
out.println("<p>");
out.println("exception.getMessage():");
out.println("<br>");
out.println(exception.getMessage());
%>
同理,尽管在servlet程序中可以向web页面中输入html内容,但还是不应该在servlet这种java文件中参入太多的html描述信息。毕竟,UI描述和业务逻辑混杂在一起会使得servlet程序难于阅读和维护修改。
// HelloWorldServlet.java
package com.servlet.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestMethodServlet extends HttpServlet {
// 客户端使用GET请求时使用该函数处理
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.write("this is doGet method");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.write("this is doPost method");
}
}
在Servlet接口中,提供了一个抽象类HTTPServlet,它是GenericServlet的子类,专门用于创建应用于HTTP协议的Servlet。此类的两大功能如下:
Servlet是一种开发动态web资源的技术,针对Servlet技术的开发,SUN公司提供了一系列接口和类,其中最重要的是javax.servlet.Servlet接口。
Servlet接口由web容器创建并调用,用于接收和响应用户的请求。
在Servlet接口中定义了5个抽象方法,它们用来描述Servlet的生命周期。
Servlet生命周期示意图如下:
可见,Servlet生命周期分为三个阶段,分别是初始化阶段,运行阶段和销毁阶段。
当客户端向Servlet容器发出HTTP请求,要求访问Servlet时,Servlet容器首先会解析请求,检查内存中是否已经存在该servlet对象,如果有就直接使用该servlet对象,如果没有就新创建servlet实例对象,然后通过调用init()方法实现该servlet的初始化工作。需要注意的是,在Servlet的整个生命周期内,它的init()方法只被调用一次。
这是servlet生命周期中最重要的阶段,在这个阶段,Servlet容器会为这个请求创建代表HTTP请求的ServletRequest对象和代表HTTP响应的ServletResponse对象,然后将它们作为参数传递servlet的service()方法。
service()方法从Service对象中获得客户请求的信息并作出相应处理,再通过ServletResponse对象生成响应结果。
在Servlet的整个生命周期内,对于Servlet的每一次访问请求,Servlet容器都会调用一次Servlet的service()方法,并且创建新的ServletRequest和ServletResponse对象,也就是说,service()方法在Servlet的整个生命周期中会被调用多次。
当服务器关闭或web应用被移除出容器时,Servlet随着web应用的销毁而被销毁。在销毁Servlet之前,Servlet容器会调用Servlet的destroy()方法,以便让Servlet对象释放它所占用的资源。
在Servlet的整个生命周期中,destroy()方法也只被调用一次。需要注意的是,Servlet对象一旦被创建就会驻留在内存中,等待客户端的访问,直到服务器关闭或web应用被移除容器时Servlet对象才被销毁。
package com.servlet.servlet;
import javax.servlet.*;
public class HelloWorldServlet extends GenericServlet {
public void init(ServletConfig config) throws ServletException {
System.out.println("init methed is called");
}
public void service(ServletRequest request, ServletResponse response)
throws ServletException {
System.out.println("Hello World");
}
public void destroy() {
System.out.println("destroy method is called");
}
}
只有将Servlet在Tomcat的web.xml文件(在WEB_INF目录下,这个目录只能在服务器端才能访问,客户端不允许访问)中映射成虚拟路径,客户端才能对其访问。
可以在一个下配置多个。
<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">
<servlet>
<servlet-name>JavaEEDemoservlet-name>
<servlet-class>com.servlet.JavaEEDemoservlet-class>
servlet>
<servlet-mapping>
<servlet-name>JavaEEDemoservlet-name>
<url-pattern>/JavaEEDemourl-pattern>
<url-pattern>/J2EEDemourl-pattern>
servlet-mapping>
web-app>
<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">
<servlet>
<servlet-name>JavaEEDemoservlet-name>
<servlet-class>com.servlet.JavaEEDemoservlet-class>
servlet>
<servlet-mapping>
<servlet-name>JavaEEDemoservlet-name>
<url-pattern>/JavaServlet/*url-pattern>
<url-pattern>*.dourl-pattern>
servlet-mapping>
web-app>
如果某个Servlet的映射路径仅仅是一个正斜线(/),那么这个Servlet就表示当前web应用程序的默认Servlet。缺省Servlet用于处理其他Servlet都不处理的访问请求。
<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">
<servlet>
<servlet-name>JavaEEDemoservlet-name>
<servlet-class>com.servlet.JavaEEDemoservlet-class>
servlet>
<servlet-mapping>
<servlet-name>JavaEEDemoservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
Servlet的配置信息封装到了一个ServletConfig对象中,ServletConfig定义了一系列获取配置信息的方法。这些方法有:
package com.servlet.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
ServletConfig config = this.getServletConfig(); // 拿到ServletConfig对象
String param = config.getInitParameter("encoding"); // 通过config对象获得参数名为encoding对应的参数值
out.println("encoding = " + param);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
当Servlet容器启动时,会为每个web应用场景一个唯一的ServletContext对象,它代表当前web应用的一个全局实例。
package com.servlet.servlet;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
ServletContext context = this.getServletContext(); // 拿到ServletContext对象
// 得到包含所有初始化参数名的Enumeration对象
Enumeration<String> paramNames = context.getInitParameterNames();
// 遍历所有的初始化参数名,得到相应的参数值,打印到控制台
out.println("all the paramName and paramValue are following:");
// 遍历所有的初始化参数名,得到相应的参数值并打印
while (paramNames.hasMoreElements()) {
String name = paramNames.nextElement();
String value = context.getInitParameter(name);
out.println(name + ": " + value);
out.println("
");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
/* TestServlet01.java */
package com.servlet.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class TestServlet01 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = this.getServletContext();
context.setAttribute("data", "this servlet save data"); // 通过setAttribute()方法设置属性值
// 这里设置的data属性值会影响之后所有servlet的相应属性
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
/* TestServlet02.java */
package com.servlet.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class TestServlet02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
ServletContext context = this.getServletContext();
String data = (String) context.getAttribute("data"); // 通过getAttribute()方法获取属性值,这里获得的属性值其实是经过上面TestServlet01设置过的
out.println(data);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
package com.servlet.servlet;
import java.io.*;
import java.util.Properties;
import javax.servlet.*;
import javax.servlet.http.*;
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = this.getServletContext();
PrintWriter out = response.getWriter();
// ServletContext对象还可以用于读取资源文件
InputStream in = context.getResourceAsStream("/WEB-INF/classes/itcast.properties");
Properties pros = new Properties();
pros.load(in);
out.println("Company = " + pros.getProperty("Company") + "
");
out.println("Address = " + pros.getProperty("Address") + "
");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
Servlet最主要的作用就是处理客户端请求,并向客户端做出响应。为此,对于Servlet接收到的每次请求,Web服务器在调用service()之前,都会创建两个对象,分别是HttpServlet和HttpServletResponse对象。
HttpServletRequest和HttpServletResponse这两个对象所扮演的角色如下:
在HttpServletResponse接口中定义了向客户端发送响应状态码,响应消息头和响应消息体的方法。
该方法用于设置HTTP响应消息的状态码,并生成响应状态行。只要通过setStatus(int status)
方法设置了状态码,即可实现状态行的发送。正常情况下,Web服务器会默认产生一个状态码为200的状态行。
该方法用于发送表示错误信息的状态码,例如,404状态码表示找不到客户端请求的资源。
该方法所获取的字节输出流对象为ServletOutputStream类型。由于ServletOutputStream是OutputStream的子类,它可以直接输出字节数组中的二进制数据。因此,要想要输出二进制格式的响应正文,就需要使用getOutputStream()方法。
package com.servlet.response;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class PrintServlet1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String data = "输出二进制数据";
OutputStream out = response.getOutputStream(); // 获取二进制输出流对象
out.write(data.getBytes()); // 输出字符串的二进制表示
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
该方法所获取的字符输出流对象为PrintWriter类型。由于PrintWriter类型的对象可以直接输出字符文本内容。因此,要想要输出内容全为字符文本的网页文档,需要使用getWriter()方法。
package com.servlet.response;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class PrintServlet2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
String data = "输出字符数据";
PrintWriter print = response.getWriter(); // 获取字符输出对象
print.write(data);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
doGet(request,response);
}
}
package com.servlet.response;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RefreshServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 2秒后刷新并跳转(到http://www.hbmy.edu.cn)
response.setHeader("Refresh", "2;URL=http://www.hbmy.edu.cn");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package com.servlet.response;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RefreshServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 每隔3秒定时刷新当前页面
response.setHeader("Refresh", "3");
response.getWriter().println(new java.util.Date()); // 输出当前时间
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package com.servlet.response;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class CacheServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.setDateHeader("Expires",0);
response.setHeader("Cache-Control","no-cache"); // 在响应头中添加Cache-Control:no-cache字段可以实现游览器进制缓存的功能
response.setHeader("Pragma","no-cache");
PrintWriter out = response.getWriter();
out.println("本次响应的随机数为:" + Math.random());
out.println("");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
package com.servlet.response;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ChineseServlet3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String data="中国";
/* 第一种设置编码的方法 */
// response.setHeader("Content-Type","text/html;charset=utf-8");
/* 第二种设置编码的方法 */
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println(data);
}
public void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
更正编码前 | 更正编码后 |
---|---|
所谓请求重定向,指的是Web服务器接受到客户端的请求后,由于某些条件限制,不能访问当前请求URL所指向的Web资源,而是指定了一个新的资源路径,让客户端重新发送请求。
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
// 用HttpServletRequest对象的getParameter()方法获取用户名和密码
String username = request.getParameter("username"); // 从表单(name属性为username)中拿到参数值(表单的value)
String password = request.getParameter("password");
// 假设用户名和密码分别为:admin 和 123456
if (("admin").equals(username) &&("123456").equals(password)) {
// 如果用户名和密码正确,重定向到web应用下的Welcome虚拟路径
response.sendRedirect("/JavaEEDemo/Welcome");
} else {
// 如果用户名和密码错误,重定向到web应用下的Error虚拟路径
response.sendRedirect("/JavaEEDemo/Error");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
在HttpServletRequest接口中定义了获取请求行,请求头和请求消息体的相关方法。
package com.servlet.request;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestLineServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
// 获取请求行的相关信息
out.println("getMethod : " + request.getMethod() + "
");
out.println("getRequestURI : " + request.getRequestURI() + "
");
out.println("getQueryString : " + request.getQueryString() + "
");
out.println("getProtocol : " + request.getProtocol() + "
");
out.println("getContextPath : " + request.getContextPath() + "
");
out.println("getPathInfo : " + request.getPathInfo() + "
");
out.println("getPathTranslated : " + request.getPathTranslated()+ "
");
out.println("getServletPath : " + request.getServletPath() + "
");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package com.servlet.request;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestHeadersServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
Enumeration headerNames = request.getHeaderNames(); // 获取请求消息中所有头字段
// 遍历所有请求头,并通过getHeader()方法获取一个指定名称的头字段
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
out.print(headerName + " : " + request.getHeader(headerName)+ "
");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
在HTTPServletRequest接口中,接收请求消息体时,定义了两个与输入流相关的方法:
该方法用于获取表示实体内容的ServletInputStream对象。
package com.servlet.request;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestBodyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
InputStream in = request.getInputStream(); // 获取输入流对象
byte[] buffer = new byte[1024]; // 定义一个容量为1024个字节的数组
StringBuilder sb = new StringBuilder(); // 创建StringBuilder对象
int len;
// 循环读取数组中的数据
while ((len = in.read(buffer)) != -1) {
sb.append(new String(buffer, 0, len));
}
out.println(sb);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
该方法用于获取表示实体内容的BufferedReader对象,该对象会将实体内容中的字节数据按照请求消息体中指定的字符集编码转换为文本字符串。
package com.servlet.request;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestParamsServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("gbk");
response.setContentType("text/html;charset=gbk"); // 设置编码,避免乱码问题
PrintWriter out = response.getWriter();
String name = request.getParameter("username"); // GET请求的参数就在url中,POST请求的参数虽经过加密,但也是这个格式
String password = request.getParameter("password");
out.println("用户名:" + name);
out.println("密 码:" + password);
String[] hobbys= request.getParameterValues("hobby");
out.print("爱好:");
for (int i = 0; i < hobbys.length; i++) {
out.print(hobbys[i]+" ");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
package com.servlet.request;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestNetServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html ;charset=utf-8");
PrintWriter out = response.getWriter();
// 通过HttpServletRequest对象输出一些网络连接的相关参数
out.println("getRemoteAddr : " + request.getRemoteAddr() + "
");
out.println("getRemoteHost : " + request.getRemoteHost() + "
");
out.println("getRemotePort : " + request.getRemotePort() + "
");
out.println("getLocalAddr : " + request.getLocalAddr() + "
");
out.println("getLocalName : " + request.getLocalName() + "
");
out.println("getLocalPort : " + request.getLocalPort() + "
");
out.println("getServerName : " + request.getServerName() + "
");
out.println("getServerPort : " + request.getServerPort() + "
");
out.println("getScheme : " + request.getScheme() + "
");
out.println("getRequestURL : " + request.getRequestURL() + "
");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}