JavaWeb应用的概念
在Sun的Java Servlet规范中,对Java Web应用作了这样定义:“Java Web应用由一组Servlet、HTML页、类、以及其它可以被绑定的资源构成。它可以在各种供应商提供的实现Servlet规范的 Servlet容器 中运行。”Java Web应用中可以包含如下内容:Servlet,JSP,实用类,静态文档(如HTML、图片等),描述Web应用的信息(web.xml)。
Servlet及Servlet容器的概念
Servlet容器为JavaWeb应用提供运行时环境,它负责管理Servlet和JSP的生命周期,以及管理它们的共享数据。Servlet容器也称为JavaWeb应用容器,或者Servlet/JSP容器。目前最流行的Servlet容器软件括:Tomcat,Resin等。J2EE服务器(如Weblogic)中也提供了内置的Servlet容器。
Servlet是和平台无关的服务器端组件,它运行在Servlet容器中。Servlet容器负责Servlet和客户的通信以及调用Servlet的方法,Servlet和客户的通信采用“请求/响应”的模式。
Servlet可完成如下功能:
创建并返回基于客户请求的动态HTML页面。
创建可嵌入到现有HTML 页面中的部分HTML 页面(HTML 片段)。
与其它服务器资源(如数据库或基于Java的应用程序)进行通信。
Servlet容器响应客户请求的过程
1) Servlet引擎检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第4)步,否则,执行第2)步。
2) Servlet引擎装载并创建该Servlet的一个实例对象:调用该 Servlet 的构造器 。
3) Servlet引擎调用Servlet实例对象的init()方法。
4) Servlet引擎创建一个用于封装请求的ServletRequest对象和一个代表响应消息的ServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
5) WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
Servlet生命周期
以下为Servlet生命周期 方法,由Servlet容器负责调用
1) 构造方法:只被调用一次,只有第一次请求Servlet时,创建Servlet的实例,调用构造器。
2) init方法:只被调用一次,在创建好实例后立即被调用,用户初始化Servlet。
3) service方法:被多次调用,每次请求都会调用此方法,实际用户响应请求。
4) destroy方法:只被调用一次,在当前Servlet所在的WEB应用被卸载前调用,用于释放当前Servlet所占用的资源。
load-on-startup 参数
<servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>org.rabbitx.web.servlet.usermgr.LoginServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
此参数可以指定Servlet被创建的时机,若为负数,则在第一次请求是被创建。若为零或正数,则当前WEB应用被Servlet容器加载时创建实例,且数值越小越早被创建。
Servlet初始换参数
1. 配置web.xml
<servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>org.rabbitx.web.servlet.usermgr.LoginServlet</servlet-class> <init-param> <param-name>errorCount</param-name> <param-value>100</param-value> </init-param> <init-param> <param-name>onlineCount</param-name> <param-value>1</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
2. 在代码中获取配置值
package org.rabbitx.web.servlet.usermgr;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
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 LoginServlet extends HttpServlet{
private static final long serialVersionUID = -83692490951802366L;
private Map<String,String> servletPara = new HashMap<String,String>();
@Override
public void init(ServletConfig config) throws ServletException {
Enumeration<String> paraNames = config.getInitParameterNames();
while(paraNames.hasMoreElements())
{
String paraName = paraNames.nextElement();
String paraValue = config.getInitParameter(paraName);
if(null != paraValue)
{
servletPara.put(paraName, paraValue);
}
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter pw = response.getWriter();
pw.println("<table border=1>");
pw.println("<tr><th>ParameterName</th><th>ParameterValue</th></tr>");
//同一个请求参数名可能出现多次
Enumeration<String> paraNames = request.getParameterNames();
while(paraNames.hasMoreElements())
{
String paraName = paraNames.nextElement();
String[] paraValue = request.getParameterValues(paraName);
if(null != paraValue)
{
for(int i = 0; i < paraValue.length; i++)
{
pw.println("<tr><td>" + paraName + "</td><td>" + paraValue[i] + "</td></tr>");
}
}
}
for(Entry<String,String> entry : servletPara.entrySet())
{
pw.println("<tr><td>" + entry.getKey() + "</td><td>" + entry.getValue() + "</td></tr>");
}
pw.println("</table>");
pw.flush();
pw.close();
}
}
Servlet的注册与运行
Servlet程序必须通过Servlet容器来启动运行,并且储存目录有特殊要求,通常需要存储在<WEB应用程序目录>\WEB-INF\classes\目录中。 Servlet程序必须在WEB应用程序的web.xml文件中进行注册和映射其访问路径,才可以被Servlet引擎加载和被外界访问。一个<servlet>元素用于注册一个Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。 一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name>和<url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。
Servlet映射的细节
同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。 在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。
<servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>org.rabbitx.web.servlet.usermgr.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
ServletAPI
ServletConfig 接口
Servlet在有些情况下可能需要访问Servlet容器或借助Servlet容器访问外部的资源,所以,Serlvet引擎需要将表示Servlet容器的对象传递给Servlet。另外,在web.xml文件中为某个Servlet设置的友好名称和初始化参数等信息也需要传递给该Servlet。Servlet引擎将代表Servlet容器的对象(ServletContext)和Servlet的配置参数信息一并封装到一个称为ServletConfig的对象中,并在初始化Servlet实例对象时传递给该Servlet。
ServletConfig接口则用于定义ServletConfig对象需要对外提供的方法,以便在Servlet程序中可以调用这些方法来获取有关信息。Servlet引擎调用Servlet的实例对象的init(ServletConfig config)方法将ServletConfig对象传递给Servlet。Servlet.getServletConfig()方法必须返回init(ServletConfig config)方法传递进来的这个ServletConfig对象的引用。
ServletConfig接口的方法有getInitParameterNames ,getInitParameter ,getServletName ,getServletContext 。
ServletContext接口
Servlet引擎为每个WEB应用程序都创建一个对应的ServletContext对象,ServletContext对象被包含在ServletConfig对象中,调用ServletConfig.getServletContext方法可以返回ServletContext对象的引用。由于一个WEB应用程序中的所有Servlet都共享同一个ServletContext对象,所以,ServletContext对象被称之为 application 对象(Web应用程序对象)。
通过ServletContext接口可以获取WEB应用程序的初始化参数,记录日志,application域范围的属性 ,访问资源文件 ,获取虚拟路径所映射的本地路径 ,实现WEB应用程序之间的访问等。
获取WEB应用程序的初始化参数
为WEB应用程序设置初始化参数的好处在于不需要修改Servlet源程序,就可以改变一些参数信息。
ServletContext.getInitParameterNames方法用于返回一个包含WEB应用程序的所有初始化参数名称的Enumeration集合对象。
ServletContext.getInitParameter方法用于返回某个指定名称的初始化参数值。
在web.xml文件的根元素<web-app>中增加<context-param>子元素,如下所示:
<context-param> <param-name>userCount</param-name> <param-value>200</param-value> </context-param>
获取配置值代码
private Map<String,String> servletPara = new HashMap<String,String>();
@Override
public void init(ServletConfig config) throws ServletException {
//获取<servlet>中<init-param></init-param>配置参数
Enumeration<String> paraNames = config.getInitParameterNames();
while(paraNames.hasMoreElements())
{
String paraName = paraNames.nextElement();
String paraValue = config.getInitParameter(paraName);
if(null != paraValue)
{
servletPara.put(paraName, paraValue);
}
}
//获取<context-param></context-param>配置参数
ServletContext servletContext = config.getServletContext();
paraNames = servletContext.getInitParameterNames();
while(paraNames.hasMoreElements())
{
String paraName = paraNames.nextElement();
String paraValue = servletContext.getInitParameter(paraName);
if(null != paraValue)
{
servletPara.put(paraName, paraValue);
}
}
}
获取虚拟路径所映射的本地路径
getRealPath(String path) 方法: 用于返回某个虚拟路径所映射的本地文件系统路径
HttpServlet的service方法
HttpServletRequest:封装了请求信息,可以从中获取到所有请求信息。
HttpServletResponse:封装了响应信息,如果想给用户什么响应,都可以通过使用该接口的方法实现。
这两个接口的具体实现都是服务器实现的,并在服务器调用service方法的传入。
HttpServletRequest简介
Servlet API 中定义的 ServletRequest 接口类用于封装请求消息。 HttpServletRequest 是专用于HTTP协议的ServletRequest 子接口,它用于封装 HTTP 请求消息。 在 service() 方法内部调用 HttpServletRequest 对象的各种方法来获取请求消息。
1) 获取请求行的相关信息
HTTP请求消息的请求行包括请求方式、资源路径和HTTP协议版本:
GET /lampbrother/servlet/RequestURI?param1=a¶m2=b HTTP/1.1
getMethod:返回HTTP请求消息中的请求方式。
getRequestURI:返回请求行中的资源名部分。
getQueryString :返回请求行中的参数部分。
getProtocol:返回请求行中的协议名和版本。
getContextPath:返回请求资源所属于的WEB应用程序的路径。
getPathInfo:返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
getPathTranslated:返回URL中的额外路径信息所对应的资源的真实路径。
getServletPath方法:Servlet的名称或Servlet所映射的路径。
2) 获取网络连接信息
getRemoteAddr方法返回发出请求的客户机的IP地址,其格式为“192.168.0.3”这种形式的字符文本。 (*)
getRemoteHost方法返回发出请求的客户机的完整主机名,即“pc1.atguigu.com”这种格式。
getRemotePort方法返回发出请求的客户机所使用的网络接口的端口号。
getLocalAddr方法返回WEB服务器上接收当前请求的网络接口的IP地址。
getLocalName方法返回WEB服务器上接收当前请求的网络接口的IP地址所对应的主机名。
getLocalPort方法返回WEB服务器上接收当前请求的网络接口的端口号。
getServerName方法返回当前请求所指向的主机名。
getServerPort方法返回当前请求所连接的服务器端口号。
getScheme方法返回请求的协议名,例如http、https或ftp。
getRequestURL方法返回客户端发出请求时的完整URL。
3) 获取请求头信息
getHeader方法
getHeaders方法
getHeaderNames方法
getIntHeader方法
getDateHeader方法
getContentType方法
getContentLength方法
getCharacterEncoding方法
4) 获取请求参数
getParameter方法:根据请求参数的名字,获取参数的值。
getParameterValues方法:根据请求参数的名字,获取参数值的数组,用户获取checkbox值
getParameterNames方法 :返回参数名的Enumeration对象
getParameterMap方法 :返回参数的键值对,key:参数值,value:参数名
getMethod()方法:获取请求方式GET/POST/PUT/...
getQueryString()方法:如果是get请求,获取url中问号后面的参数的字符串,即参数字符串
getSerlvetPath()方法: 获取请求的Servlet的映射路径
获取请求参数的编程实例(http://localhost:8080/org.rabbitx.web.servlet/login?username=rabbitx&password=123456&role=admin)
package org.rabbitx.web.servlet.usermgr;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet{
private static final long serialVersionUID = -83692490951802366L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter pw = response.getWriter();
pw.println("<table border=1>");
pw.println("<tr><th>ParameterName</th><th>ParameterValue</th></tr>");
//同一个请求参数名可能出现多次
Enumeration<String> paraNames = request.getParameterNames();
while(paraNames.hasMoreElements())
{
String paraName = paraNames.nextElement();
String[] paraValue = request.getParameterValues(paraName);
if(null != paraValue)
{
for(int i = 0; i < paraValue.length; i++)
{
pw.println("<tr><td>" + paraName + "</td><td>" + paraValue[i] + "</td></tr>");
}
}
}
pw.println("</table>");
pw.flush();
pw.close();
}
}
5) 请求域属性
存储在ServletRequest对象中的对象称之为请求域属性,属于同一个请求的多个处理模块之间可以通过请求域属性来传递对象数据。
与请求域属性相关的方法:
setAttribute方法
getAttribute方法
removeAttribute方法
getAttributeNames方法
HttpServletResponse简介
Servlet API中定义的ServletResponse接口类用于创建响应消息。 HttpServletResponse是专用于HTTP协议的ServletResponse子接口,它用于封装HTTP响应消息。
getWriter():获取PrintWriter对象,用户直接向客户端发送数据;
setContentType(String mimeType):设置返回数据类型,如设置返回word文档:response.setContentType("application/msword");具体可以返回类型的参数值可以在web.xml中查询。
请求重定向与请求转发
1) RequestDispatcher接口
RequestDispatcher实例对象是由Servlet引擎创建的,它用于包装一个要被其他资源调用的资源(例如,Servlet、HTML文件、JSP文件等),并可以通过其中的方法将客户端的请求转发给所包装的资源。
RequestDispatcher接口中定义了两个方法:forward方法和include方法。
forward和include方法接收的两个参数必须是传递给当前Servlet的service方法的那两个ServletRequest和ServletResponse对象,或者是对它们进行了包装的ServletRequestWrapper 或ServletResponseWrapper对象。
获取RequestDispatcher对象的方法:
ServletContext.getRequestDispatcher (参数只能是以“/”开头的路径)
ServletContext.getNamedDispatcher
ServletRequest.getRequestDispatcher (参数可以是不以“/”开头的路径)
2) 用sendRedirect方法实现请求重定向
sendRedirect 方法不仅可以重定向到当前应用程序中的其他资源,它还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。 如果传递给sendRedirect 方法的相对URL以“/”开头,则是相对于整个WEB站点的根目录,而不是相对于当前WEB应用程序的根目录。
3) 请求重定向与请求转发的比较
RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。 如果传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建RequestDispatcher对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。 调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。 RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。
application域范围的属性
application 对象(ServletContext对象)内部有一个哈希表集合对象,存储进application对象内的哈希表集合对象中的每对关键字/值被称为application对象的属性。 存储在application对象中的属性也被称之为application域范围的属性,application域范围的属性可以被当作该WEB应用程序范围内的全局变量使用。
ServletContext接口中定义了4个分别用于增加、删除、访问application域范围的属性的方法:
getAttributeNames方法
getAttribute方法
removeAttribute方法
setAttribute方法
WEB中路径
getRealPath(String path):获取当前WEB应用某个文件在服务器上的绝对路径,而不是部署前的路径。
代码:servletContext.getContextPath("index.html")
getContextPath():获取当前WEB应用的名称
代码:servletContext.getContextPath()
getResourceAsStream(String path):path的/为当前WEB应用的根目录
代码:InputStream is = servletContext.getResourceAsStream("/WEB-INF/classes/jdbc.properties");
GenericServlet
1). 是一个 Serlvet. 是 Servlet 接口和 ServletConfig 接口的实现类. 但是一个抽象类. 其中的 service 方法为抽象方法
2). 如果新建的 Servlet 程序直接继承 GenericSerlvet 会使开发更简洁.
3). 具体实现:
①. 在 GenericServlet 中声明了一个 SerlvetConfig 类型的成员变量, 在 init(ServletConfig) 方法中对其进行了初始化
②. 利用 servletConfig 成员变量的方法实现了 ServletConfig 接口的方法
③. 还定义了一个 init() 方法, 在 init(SerlvetConfig) 方法中对其进行调用, 子类可以直接覆盖 init() 在其中实现对 Servlet 的初始化.
④. 不建议直接覆盖 init(ServletConfig), 因为如果忘记编写 super.init(config); 而还是用了 SerlvetConfig 接口的方法,
则会出现空指针异常.
⑤. 新建的 init(){} 并非 Serlvet 的生命周期方法. 而 init(ServletConfig) 是生命周期相关的方法.
public abstract class GenericServlet implements Servlet, ServletConfig {
/** 以下方法为 Servlet 接口的方法 **/
@Override
public void destroy() {}
@Override
public ServletConfig getServletConfig() {
return servletConfig;
}
@Override
public String getServletInfo() {
return null;
}
private ServletConfig servletConfig;
@Override
public void init(ServletConfig arg0) throws ServletException {
this.servletConfig = arg0;
init();
}
public void init() throws ServletException{}
@Override
public abstract void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException;
/** 以下方法为 ServletConfig 接口的方法 **/
@Override
public String getInitParameter(String arg0) {
return servletConfig.getInitParameter(arg0);
}
@Override
public Enumeration getInitParameterNames() {
return servletConfig.getInitParameterNames();
}
@Override
public ServletContext getServletContext() {
return servletConfig.getServletContext();
}
@Override
public String getServletName() {
return servletConfig.getServletName();
}
}
HttpServlet
1). 是一个 Servlet, 继承自 GenericServlet. 针对于 HTTP 协议所定制.
2). 在 service() 方法中直接把 ServletReuqest 和 ServletResponse 转为 HttpServletRequest 和 HttpServletResponse.
并调用了重载的 service(HttpServletRequest, HttpServletResponse)
在 service(HttpServletRequest, HttpServletResponse) 获取了请求方式: request.getMethod(). 根据请求方式有创建了
doXxx() 方法(xxx 为具体的请求方式, 比如 doGet, doPost)
3). 实际开发中, 直接继承 HttpServlet, 并根据请求方式复写 doXxx() 方法即可.
4). 好处: 直接由针对性的覆盖 doXxx() 方法; 直接使用 HttpServletRequest 和 HttpServletResponse, 不再需要强转.
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1. 获取请求方式.
String method = request.getMethod();
//2. 根据请求方式再调用对应的处理方法
if("GET".equalsIgnoreCase(method)){
doGet(request, response);
}else if("POST".equalsIgnoreCase(method)){
doPost(request, response);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
// TODO Auto-generated method stub
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
}