软件编程体系
B\S 系统架构与C\S 系统结构
Web服务器
HTTP 协议:Web 浏览器与 web 服务器的交互所遵循的规则.
Web 服务器:Web服务器可以解析HTTP协议。当Web服务器接收到一个HTTP请求(request),会返回一个HTTP响应(response)。为了处理一个请求,Web服务器可以响应一个静态页面或图片,或进行页面跳转,或者把动态响应的产生委托(delegate)给一些其它的程序例如CGI脚本,JSP脚本,ASP脚本等。无论脚本的目的何,这些服务器端(server-side)的程序通常产生一个 HTML的响应来让浏览器可以浏览。
JavaWeb应用的概念
Java Web应用由一组Servlet、HTML页、类、 以及其它可以被绑定的资源构成。它可以在各种供应商提供的实现Servlet规范的Servlet容器中运行。
Java Web应用中可以包含如下内容: Servlet,JSP,实用类,静态文档如HTML、图片等,描述Web应用的信息(web.xml)。
Servlet 容器
Servlet容器为JavaWeb应用提供运行时环境,它负责管理Servlet和JSP的生命周期,以及管理它们的共享数据。Servlet容器也称为JavaWeb应用容器,或者Servlet/JSP容器。目前最流行的Servlet容器软件括:Tomcat,Resin,J2EE服务器(如Weblogic)中也提供了内置的Servlet容器。
Web 程序结构
一个 web 应用程序是由一组 Servlet,HTML 页面,类,以及其它的资源组成的运行在 web 服务器上的完整的应用程序,以一种结构化的有层次的目录形式存在,组成 web 应用程序的这些文件要部署在相应的目录层次中,根目录代表整个 web 应用程序的”根”,通常将 web 应用程序的目录放在 webapps 目录下,在 webapps 目录下的每一个子目录都是一个独立的web 应用程序,子目录的名字就是 web 应用程序的名字,也就是 web 应用程序的“根”。用户通过 web 应用程序的”根”来访问 web 应用程序中的资源。
WEB-INF目录下的classes和lib目录都可以存放Java的类文件,在Servlet容器运行时,Web应用程序的类加载器将首先加载classes目录下的,其次才是lib目录下的类。如果这两个目录下存在同名的类,起作用的将是classes目录下的类。WEB-INF 是一个特殊的目录(所有字母都要大写)。这个目录并不属于Web应用程序可以访问的上下文路径的一部分,对客户端来说,这个目录是不可见的,但该目录下的内容对于Servlet代码是可见的。
配置任意目录下的Web应用程序
在Web服务器中可以配置虚拟目录,而虚拟目录所对应的真实目录可以在任何路径下。在Tomcat服务器中,主要在 XML 配置文件中通过<Context>元素的设置来完成的,一个<Context>元素就表示一个Web应用程序,运行在特定的虚拟主机中。<Context>元素是<Host>元素的子元素,可以在conf\server.xml文件中设置Context元素:
<Context>元素的常用属性:
使用docBase属性指定Web应用程序的真实路径。将属性reloadable设置为true,Tomcat在运行时会自动监测Servlet类的改动,如果发现有类被更新,Tomcat服务器将自动重新加载该Web应用程序。这样,在开发时,就不需要频繁重启Tomcat。
还可以在conf目录下依次创建Catalina\localhost目录,然后在localhost目录下为 test 这个Web应用程序建立 test.xml 文件,编辑这个文件输入以下内容
从Tomcat 5开始,不建议直接在server.xml文件中配置<Context>元素,因为server.xml文件作为Tomcat的主要配置文件,一旦Tomcat启动后,将不会再读取这个文件,因此无法在Tomcat服务器启动时发布Web应用程序。如果在其他地方配置<Context>元素,那么在Tomcat运行时,也可以发布Web应用程序。从Tomcat 5.5开始,在\conf\Catalina\localhost目录下创建XML配置文件来配置Web应用程序,Tomcat将以XML文件的文件名将作为Web应用程序的上下文路径,而不理会在<Context>元素的path属性中指定的上下文路径是什么。由于Tomcat 5.5之后的版本是以XML配置文件的文件名作为Web应用程序的上下文路径的,因此在配置<Context>元素时,可以不使用path属性。
Servlet简介
Java Servlet是和平台无关的服务器端组件,它运行在Servlet容器中。Servlet容器负责Servlet和客户的通信以及调用Servlet的方法,Servlet和客户的通信采用“请求/响应”的模式。 Servlet可完成如下功能:1)创建并返回基于客户请求的动态HTML页面;2)创建可嵌入到现有HTML 页面中的部分HTML 页面(HTML 片段)3)与其它服务器资源(如数据库或基于Java的应用程序)进行通信。
Servlet容器响应客户请求的过程: ServletAPI:
Servlet容器响应客户请求的过程
①Servlet引擎检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
②装载并创建该Servlet的一个实例对象:调用该 Servlet 的构造器
③调用Servlet实例对象的init()方法。
④创建一个用于封装请求的ServletRequest对象和一个代表响应消息的ServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
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的对外访问路径。
<web-app>
......
<servlet>
<servlet-name>AnyName</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
......
<servlet-mapping>
<servlet-name>AnyName</servlet-name>
<url-pattern>/demo/hello.html</url-pattern>
</servlet-mapping>
......
</web-app>
Servlet映射的细节
同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。
在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。
<servlet-mapping>
<servlet-name>
AnyName
</servlet-name>
<url-pattern>
*.do
</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>
AnyName
</servlet-name>
<url-pattern>
/action/*
</url-pattern>
</servlet-mapping>
缺省Servlet
如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。
凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。
在<tomcat的安装目录>\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet。
当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet。
Servlet的线程安全问题
Servlet引擎采用多线程模式运行,它为并发的每个访问请求都使用一个独立的线程来进行响应,但带来了线程安全的问题。
如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。
SingleThreadModel接口中没有定义任何方法,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。
对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。
实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上的多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。
ServletConfig接口
Servlet在有些情况下可能需要访问Servlet容器或借助Servlet容器访问外部的资源,所以,Serlvet引擎需要将表示Servlet容器的对象传递给Servlet。另外,在web.xml文件中为某个Servlet设置的友好名称和初始化参数等信息也需要传递给该Servlet。
为Servlet设置参数的例子:
<servlet>
<servlet-name>ConfigTest</servlet-name>
<servlet-class>ConfigTestServlet</servlet-class>
<init-param>
<param-name>Corporation</param-name>
<param-value>传智播客公司</param-value>
</init-param>
</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
GenericServlet类实现ServletConfig接口的目的
Servlet接口中定义了一个getServletConfig方法,该方法必须返回Servlet容器调用Servlet.init(ServletConfig config)方法时传递进来的那个ServletConfig对象的引用,GenericServlet类已经按此要求实现了getServletConfig方法。
在Servlet程序中如何调用ServletConfig对象的方法
String servletName = getServletConfig().getServletName();
GenericServlet类如何实现ServletConfig接口中的方法
public String getServletName()
{
return getServletConfig().getServletName();
}
在Servlet程序中调用ServletConfig对象的方法的简单方式
String servletName = getServletName();
service方法的实现技巧
//Servlet接口中定义的service方法的语法格式及处理代码:
public void public void service(ServletRequest req,ServletResponse res)
throws ServletException,java.io.IOException
{
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
……
request.HttpServletRequest中定义的方法();
response.HttpServletResponse中定义的方法();
……
}
//HttpServlet类中定义的重载service方法的语法格式及处理代码:
protected void service(HttpServletRequest req,HttpServletResponse res)
throws ServletException,java.io.IOException
{
……
req.HttpServletRequest中定义的方法();
res.HttpServletResponse中定义的方法();
……
}
//HttpServlet类中实现的service方法的语法格式及处理代码:
public void service(ServletRequest req,ServletResponse res)
throws ServletException,java.io.IOException
{
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
service(request,response);//这里调用的是那个重载的service方法
}
doXxx方法
如果要在service方法中对不同的请求方式进行不同的处理,那么首先必须判断客户端的请求方式,然后通过选择语句对每种方式分别进行处理。
HttpServlet类为每一种HTTP请求方式都定义了一个对应的doXxx方法,例如,与GET请求方式对应的是doGet方法,与POST请求方式对应的是doPost方法。
HttpServlet中重载的Service方法根据客户端的请求方式,分别调用与之对应的doXxx方法来完成具体的处理和响应细节,并将它接受的两个参数传递给该doXxx方法,例如,如果HTTP请求方式为GET,则调用doGet方法。
HttpServletRequest
Servlet API中定义的ServletRequest接口类用于封装请求消息。
HttpServletRequest是专用于HTTP协议的ServletRequest子接口,它用于封装HTTP请求消息。
在service()方法内部调用HttpServletRequest对象的各种方法来获取请求消息。
获取请求行的相关信息
HTTP请求消息的请求行包括请求方式、资源路径和HTTP协议版本:
GET /it315/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所映射的路径。
获取网络连接信息
getRemoteAddr方法返回发出请求的客户机的IP地址,其格式为“192.168.0.3”这种形式的字符文本。 (*)
getRemoteHost方法返回发出请求的客户机的完整主机名,即“pc1.it315.org”这种格式。
getRemotePort方法返回发出请求的客户机所使用的网络接口的端口号。
getLocalAddr方法返回WEB服务器上接收当前请求的网络接口的IP地址。
getLocalName方法返回WEB服务器上接收当前请求的网络接口的IP地址所对应的主机名。
getLocalPort方法返回WEB服务器上接收当前请求的网络接口的端口号。
getServerName方法返回当前请求所指向的主机名。
getServerPort方法返回当前请求所连接的服务器端口号。
getScheme方法返回请求的协议名,例如http、https或ftp。
getRequestURL方法返回客户端发出请求时的完整URL。
获取请求头信息
getHeader方法
getHeaders方法
getHeaderNames方法
getIntHeader方法
getDateHeader方法
getContentType方法
getContentLength方法
getCharacterEncoding方法
使用GET方式传递参数
在浏览器地址栏中输入某个URL地址或单击网页上的一个超链接时,浏览器发出的HTTP请求消息的请求方式为GET。
如果网页中的<form>表单元素的method属性被设置为了“GET”,浏览器提交这个FORM表单时生成的HTTP请求消息的请求方式也为GET。
使用GET请求方式给WEB服务器传递参数的格式:
http://www.it315.org/counter.jsp?name=zhangsan&password=123
使用GET方式传送的数据量一般限制在1KB以下。
使用POST方式传递参数
POST请求方式主要用于向WEB服务器端程序提交FORM表单中的数据。
POST方式将各个表单字段元素及其数据作为HTTP消息的实体内容发送给WEB服务器,传送的数据量要比使用GET方式传送的数据量大得多.
//POST请求消息的格式:
POST /counter.jsp HTTP/1.1
referer: http://localhost:8080/Register.html
content-type: application/x-www-form-urlencoded
host: localhost:8080
content-length: 43
name=zhangsan&password=123
获取请求参数
getParameter方法
getParameterValues方法
getParameterNames方法
getParameterMap方法
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements())
{
String paramName = (String)paramNames.nextElement();
out.print(paramName + " : " + request.getParameter(paramName) + "<br>");
/*如果要考虑同一个请求头名可能出现多次,那么应该用下面的代码段代替上面一行程序代码*/
/*String [] paramValues = request.getParameterValues(paramName);
//良好的编程习惯,在使用对象和数组之前先判断其是否为null
if(paramValues != null)
{
for(int i=0;i<paramValues.length;i++)
{
out.print(paramName + " : " + (String)paramValues[i] + "<br>");
}
}*/
}
请求域属性
存储在ServletRequest对象中的对象称之为请求域属性,属于同一个请求的多个处理模块之间可以通过请求域属性来传递对象数据。
//与请求域属性相关的方法:
setAttribute方法
getAttribute方法
removeAttribute方法
getAttributeNames方法
请求域属性的典型应用:
MVC设计模式将一次请求的响应过程分成三个功能模块(一般称之为层)来协同完成,这三个模块分别是Model(模型层)、View(视图层)、Controller(控制层)。
Model是可作为JavaBean使用的业务对象;View是负责创建显示界面的JSP页面;Controller通常是一个接收用户请求的Servlet程序,它根据请求创建相应的model对象和调用model对象的业务方法,最后再选择一个View去创建网页文档内容并回送给客户端。
Controller调用RequestDispatcher.forward方法将请求转发给作为View的JSP页面,同时将Model对象作为请求域属性传递过去,作为View的JSP页面再从请求域中检索出Model对象。
采用MVC设计模式实现一个简单示例程序:
UserRegister.html是一个包含有让用户填写注册信息的FORM表单的HTML页面;
User.java是一个代表用户注册信息的普通Java类;
ActionServlet.java是一个用于处理FORM表单信息的Servlet程序,它根据表单提交的信息创建一个User类的实例对象,并把这个User实例对象存储到请求域中,然后将请求转发给另外一个用于显示用户注册信息的Servlet程序;
JspResultServlet.java是一个用于显示用户注册信息的Servlet程序,JspResultServlet从请求域中取出User实例对象,并显示出这个User实例对象的信息。
HttpServletResponse
Servlet API中定义的ServletResponse接口类用于创建响应消息。
HttpServletResponse是专用于HTTP协议的ServletResponse子接口,它用于封装HTTP响应消息。
请求重定向与请求转发
RequestDispatcher接口
用forward方法实现请求转发,请求转发的运行流程
用sendRedirect方法实现请求重定向,请求重定向的运行流程
请求重定向与请求转发的比较
RequestDispatcher接口
RequestDispatcher实例对象是由Servlet引擎创建的,它用于包装一个要被其他资源调用的资源(例如,Servlet、HTML文件、JSP文件等),并可以通过其中的方法将客户端的请求转发给所包装的资源。
RequestDispatcher接口中定义了两个方法:forward方法和include方法。
forward和include方法接收的两个参数必须是传递给当前Servlet的service方法的那两个ServletRequest和ServletResponse对象,或者是对它们进行了包装ServletRequestWrapper 或ServletResponseWrapper对象。
//获取RequestDispatcher对象的方法:
ServletContext.getRequestDispatcher //(参数只能是以“/”开头的路径)
ServletContext.getNamedDispatcher
ServletRequest.getRequestDispatcher //(参数可以是不以“/”开头的路径)
用sendRedirect方法实现请求重定向
sendRedirect 方法不仅可以重定向到当前应用程序中的其他资源,它还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
如果传递给sendRedirect 方法的相对URL以“/”开头,则是相对于整个WEB站点的根目录,而不是相对于当前WEB应用程序的根目录。
请求重定向与请求转发的比较
1)RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
2)如果传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建RequestDispatcher对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。
3)调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。
4)HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。
5)RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。
ServletContext接口
Servlet引擎为每个WEB应用程序都创建一个对应的ServletContext对象,ServletContext对象被包含在ServletConfig对象中,调用ServletConfig.getServletContext方法可以返回ServletContext对象的引用。由于一个WEB应用程序中的所有Servlet都共享同一个ServletContext对象,所以,ServletContext对象被称之为 application 对象(Web应用程序对象)。
功能:
1)获取WEB应用程序的初始化参数
2)记录日志
3)application域范围的属性
4)访问资源文件
5)获取虚拟路径所映射的本地路径
6)WEB应用程序之间的访问
7)ServletContext的其他方法
获取WEB应用程序的初始化参数
为WEB应用程序设置初始化参数的好处在于不需要修改Servlet源程序,就可以改变一些参数信息。
ServletContext.getInitParameterNames方法用于返回一个包含WEB应用程序的所有初始化参数名称的Enumeration集合对象,ServletContext.getInitParameter方法用于返回某个指定名称的初始化参数值。
在web.xml文件的根元素<web-app>中增加<context-param>子元素,如下所示:
<context-param>
<param-name>companyName</param-name>
<param-value>it315_new</param-value>
</context-param>
application域范围的属性
application 对象(ServletContext对象)内部有一个哈希表集合对象,存储进application对象内的哈希表集合对象中的每对关键字/值被称为application对象的属性。存储在application对象中的属性也被称之为application域范围的属性,application域范围的属性可以被当作该WEB应用程序范围内的全局变量使用。
ServletContext接口中定义了4个分别用于增加、删除、访问application域范围的属性的方法:
getAttributeNames方法
getAttribute方法
removeAttribute方法
setAttribute方法
获取虚拟路径所映射的本地路径
getRealPath(String path) 方法: 用于返回某个虚拟路径所映射的本地文件系统路径