Java Web基础
做服务器端web开发的一整套JAVA工具,里面包含了各种服务、接口和协议。其中就包含了十三种技术规范:
JDBC, JNDI, EJBs, RMI, JSP, Java servlets, XML, JMS, Java IDL, JTS, JTA, JavaMail 和 JAF.
WEB标准不是某一个标准,而是一系列标准的集合。网页主要由三部分组成:结构(Structure)、表现(Presentation)和行为(Behavior)。对应的标准也分三方面:结构化标准语言主要包括XHTML和XML,表现标准语言主要包括CSS,行为标准主要包括对象模型(如W3C DOM)、ECMAScript等。这些标准大部分由万维网联盟(外语缩写:W3C)起草和发布,也有一些是其他标准组织制订的标准,比如ECMA(European Computer Manufacturers Association)的ECMAScript标准。
转发(forward)和重定向(redirect)
forward是容器中控制权的转向,是服务器请求资源,服务器直接访问目标地址的URL,把那个URL 的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。redirect就是服务器端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,因此从浏览器的地址栏中可以看到跳转后的链接地址,很明显redirect无法访问到服务器保护起来资源,但是可以从一个网站redirect到其他网站。forward更加高效,所以在满足需要时尽量使用forward(通过调用RequestDispatcher对象的forward()方法,该对象可以通过ServletRequest对象的getRequestDispatcher()方法获得),并且这样也有助于隐藏实际的链接;在有些情况下,比如需要访问一个其它服务器上的资源,则必须使用重定向(通过HttpServletResponse对象调用其sendRedirect()方法实现)。
两个重要的技术:JSP和Servlet技术
Servlet(Server Applet)
全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。——wikipedia。 嗯,就认为它是一个提供动态内容的技术吧。
Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 Java Web 技术的核心基础。因而掌握 Servlet 的工作原理是成为一名合格的 Java Web 技术开发人员的基本要求。
JSP(JavaServer Pages)
是动态生成HTML、XML或其他格式文档的Web网页的技术标准。 ——wikipedia
同样是提供动态内容,和Servlet有什么关系呢? 这么说吧: 毕竟观众姥爷更加喜欢HTML的页面,而使用Servlet输出HTML很麻烦,JSP技术相当于一个 中间件 ,连接HTML和Servlet,在JSP页面可以插入JAVA代码,然后通过JSP编译器把JSP编译成一个Java Servlet,最终对外提供服务。
JSP有9个内置对象:
- request:封装客户端的请求,其中包含来自GET或POST请求的参数;
- response:封装服务器对客户端的响应;
- pageContext:通过该对象可以获取其他对象;
- session:封装用户会话的对象;
- application:封装服务器运行环境的对象;
- out:输出服务器响应的输出流对象;
- config:Web应用的配置对象;
- page:JSP页面本身(相当于Java程序中的this);
- exception:封装页面抛出异常的对象。
补充:如果用Servlet来生成网页中的动态内容无疑是非常繁琐的工作,另一方面,所有的文本和HTML标签都是硬编码,即使做出微小的修改,都需要进行重新编译。JSP解决了Servlet的这些问题,它是Servlet很好的补充,可以专门用作为用户呈现视图(View),而Servlet作为控制器(Controller)专门负责处理用户请求并转发或重定向到某个页面。基于Java的Web开发很多都同时使用了Servlet和JSP。JSP页面其实是一个Servlet,能够运行Servlet的服务器(Servlet容器)通常也是JSP容器,可以提供JSP页面的运行环境,Tomcat就是一个Servlet/JSP容器。第一次请求一个JSP页面时,Servlet/JSP容器首先将JSP页面转换成一个JSP页面的实现类,这是一个实现了JspPage接口或其子接口HttpJspPage的Java类。JspPage接口是Servlet的子接口,因此每个JSP页面都是一个Servlet。转换成功后,容器会编译Servlet类,之后容器加载和实例化Java字节码,并执行它通常对Servlet所做的生命周期操作。对同一个JSP页面的后续请求,容器会查看这个JSP页面是否被修改过,如果修改过就会重新转换并重新编译并执行。如果没有则执行内存中已经存在的Servlet实例。我们可以看一段JSP代码对应的Java程序就知道一切了,而且9个内置对象的神秘面纱也会被揭开。
JSP页面:
<%@ page pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath%>">
<title>首页</title>
<style type="text/css">
* { font-family: "Arial"; }
</style>
</head>
<body>
<h1>Hello, World!</h1>
<hr/>
<h2>Current time is: <%= new java.util.Date().toString() %></h2>
</body>
</html>
对应的Java代码:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/7.0.52
* Generated at: 2014-10-13 13:28:38 UTC
* Note: The last modified time of this file was set to
* the last modified time of the source file after
* generation to assist with modification tracking.
*/
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static final javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory
.getDefaultFactory();
private static java.util.Map<java.lang.String, java.lang.Long> _jspx_dependants;
private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String, java.lang.Long> getDependants() {
return _jspx_dependants;
}
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(
getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory
.getInstanceManager(getServletConfig());
}
public void _jspDestroy() {
}
public void _jspService(
final javax.servlet.http.HttpServletRequest request,
final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
// 内置对象就是在这里定义的
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html;charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write('\r');
out.write('\n');
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
// 以下代码通过输出流将HTML标签输出到浏览器中
out.write("\r\n");
out.write("\r\n");
out.write("<!DOCTYPE html>\r\n");
out.write("<html>\r\n");
out.write(" <head>\r\n");
out.write(" <base href=\"");
out.print(basePath);
out.write("\">\r\n");
out.write(" <title>首页</title>\r\n");
out.write(" <style type=\"text/css\">\r\n");
out.write(" \t* { font-family: \"Arial\"; }\r\n");
out.write(" </style>\r\n");
out.write(" </head>\r\n");
out.write(" \r\n");
out.write(" <body>\r\n");
out.write(" <h1>Hello, World!</h1>\r\n");
out.write(" <hr/>\r\n");
out.write(" <h2>Current time is: ");
out.print(new java.util.Date().toString());
out.write("</h2>\r\n");
out.write(" </body>\r\n");
out.write("</html>\r\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)) {
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
out.clearBuffer();
} catch (java.io.IOException e) {
}
if (_jspx_page_context != null)
_jspx_page_context.handlePageException(t);
else
throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
servlet容器
Servlet Container(Servlet 容器) 是 Web 服务器或者应用服务器的一部分,用于提供基于请求/响应发送模式的网络服务,解码基于 MIME 的请求,并且格式化基于 MIME 的响应。Servlet 容器同时也包含和管理他们的生命周期里 Servlet。
Servlet 与 Servlet 容器的关系有点像枪和子弹的关系,枪是为子弹而生,而子弹又让枪有了杀伤力。虽然它们是彼此依存的,但是又相互独立发展,这一切都是为了适应工业化生产的结果。从技术角度来说是为了解耦,通过标准化接口来相互协作。既然接口是连接 Servlet 与 Servlet 容器的关键,那我们就从它们的接口说起。
前面说了 Servlet 容器作为一个独立发展的标准化产品,目前它的种类很多,但是它们都有自己的市场定位,很难说谁优谁劣,各有特点。例如现在比较流行的 Jetty,在定制化和移动领域有不错的发展.
Servlet 容器可以嵌入到宿主的 Web 服务器中,或者通过 Web 服务器的本地扩展 API 单独作为附加组件安装。Servelt 容器也可能内嵌或安装到启用 Web 功能的应用服务器中。
所有的 Servlet 容器必须支持 HTTP 协议用于请求和响应,但额外的基于 请求/响应 的协议,如 HTTPS (HTTP over SSL)的支持是可选的。对于 HTTP 规范需要版本,容器必须支持 HTTP/1.0 和 HTTP/1.1。因为容器或许支持 RFC2616 (HTTP/1.1)描述的缓存机制,缓存机制可能在将客户端请求交给 Servlet 处理之前修改它们,也可能在将 Servlet 生成的响应发送给客户端之前修改它们,或者可能根据 RFC2616 规范直接对请求作出响应而不交给 Servlet 进行处理。
Servlet 容器应该使 Servlet 执行在一个安全限制的环境中。在 Java 平台标准版(J2SE, v.1.3 或更高) 或者 Java平台企业版(Java EE, v.1.3 或更高) 的环境下,这些限制应该被放置在 Java 平台定义的安全许可架构中。比如,高端的应用服务器为了保证容器的其他组件不受到负面影响可能会限制 Thread 对象的创建。
Java SE 7 是构建 Servlet 容器最低的 Java平 台版本。
一个例子
以下是一个典型的事件序列:
- 客户端(如 web 浏览器)要访问 Web 服务器,并发送一个 HTTP 请求;
- Web 服务器接收到请求并且交给 servlet 容器处理,servlet 容器可以运行在与宿主 Web 服务器同一个进程中,也可以是同一主机的不同进程,或者位于不同的主机的 Web 服务器中,对请求进行处理。
- servlet 容器根据 servlet 配置选择相应的 servlet,并调用代表请求和响应的对象。
- servlet 通过请求对象得到远程用户,HTTP POST 参数和其他有关数据可能作为请求的一部分随请求一起发送过来。
- Servlet 执行我们编写的任意的逻辑,然后动态产生响应内容发送回客户端。发送数据到客户端是通过响应对象完成的。
- 一旦 servlet 完成请求的处理,servlet 容器必须确保响应正确的输出,并且将控制权还给宿主 Web 服务器。
一个Servlet容器就实现了JAVA EE中有关servlet的规范,并且容器中提供某种机制(比如Servlet引擎)负责管理servlet的整个生命周期。
例子
Tomcat提供了一个免费的、开源的Servlet&JSP容器。比如 Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。在Tomcat的配置文件(tomcat-home\conf\server.xml)中可以看到,Service元素就是整个Servlet容器,其子元素Engine就是Servlet引擎。
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<!-- Something else -->
<Service name="Catalina">
<!-- Something else -->
<Engine name="Catalina" defaultHost="localhost">
<!-- Something else -->
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<!-- Something else -->
</Host>
</Engine>
</Service>
</Server>
Jetty也提供了一个开源的servlet&JSP容器。它的架构简单、可扩展性强。
Servlet 与其他技术的对比
从功能上看,servlet 位于Common Gateway Interface(公共网关接口,简称 CGI)程序和私有的服务器扩展如 Netscape Server API(NSAPI)或 Apache Modules 这两者之间。
相对于其他服务器扩展机制 Servlet 有如下优势:
它们通常比 CGI 脚本更快,因为采用不同的处理模型。 它们采用标准的 API 从而支持更多的Web 服务器。 它们拥有 Java 编程语言的所有优势,包括容易开发和平台无关。 它们可以访问 Java 平台提供的大量的 API。
Servlet和CGI的区别
Servlet与CGI的区别在于Servlet处于服务器进程中,它通过多线程方式运行其service()方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于Servlet。
Sun Microsystems公司在1996年发布Servlet技术就是为了和CGI进行竞争,Servlet是一个特殊的Java程序,一个基于Java的Web应用通常包含一个或多个Servlet类。Servlet不能够自行创建并执行,它是在Servlet容器中运行的,容器将用户的请求传递给Servlet程序,并将Servlet的响应回传给用户。通常一个Servlet会关联一个或多个JSP页面。以前CGI经常因为性能开销上的问题被诟病,然而Fast CGI早就已经解决了CGI效率上的问题,所以面试的时候大可不必信口开河的诟病CGI,事实上有很多你熟悉的网站都使用了CGI技术。
WEB服务器
可以处理HTTP协议的请求和响应,使协议和数据解耦。WEB页面内容分动态内容和静态内容。WEB容器可能自带Servlet容器,也可能没有Servlet容器的功能。如果没有,一般将第三方的Servlet容器作为组建整合到web容器。 举例
Apache是世界使用排名第一的Web服务器软件。它是一个能处理静态资源的web服务器。如果需要提供动态资源,如上面所述,它需要整合另外的servlet容器(如Tomcat、Jetty等)。
举例
Tomcat本身也提供HTTP请求访问,但是静态页面效率、可配置性、稳定性都不如Apache。通常WEB服务器会将两者相结合。运行Tomcat的时候,会有一个单独的Apache服务器进程单独运行。配置正确、正常工作时,静态资源由Apache提供,而动态资源(JSP等)由Tomcat提供。
应用服务器
什么是应用服务器?
可以通过各种协议把商业逻辑暴露给客户端的程序。JAVA应用服务器实现了JAVA EE技术规范中的部分或者全部API的功能。
举例
WebLogic Server 是商业市场上主要的Java EE应用服务器软件之一,是世界上第一个成功商业化的J2EE应用服务器。它支持全部的十三项技术规范。(很吊啊有木有,可惜商业化的,很贵的说)
举例
Nginx服务器 是一款轻量级的Web服务器/反向代理服务器及电子邮件代理服务器。(这里反向代理服务器和电子邮件代理服务器属于应用服务器的范畴)。公司内部使用nginx,多用来进行反向代理,用来做负载均衡等。
举例
JBoss服务器 是一个基于J2EE的免费的开放源代码的应用服务器。JBoss是一个管理EJB的容器和服务器,但JBoss核心服务不包括支持servlet/JSP的WEB容器,一般与Tomcat或Jetty绑定使用。
WEB服务器和应用服务器两者对比
如果把web称作使用HTTP协议的应用,WEB服务器也可以说是应用服务器。 严格意义上来说WEB服务器只提供静态内容,而JSP/Servlet、ASP(微软开发的动态服务器页面)、PHP(PHP语言处理的动态页面)等动态内容会交给其他程序去处理。
应用服务器一般也支持HTTP协议,不过有的还支持商业逻辑。打个比方就是web容器仅仅支持HTTP协议的请求。应用服务器不光支持HTTP协议,还可以支持FTP协议成为FTP应用服务器,也可以是自定义的协议,比如网络游戏等。 易混淆的地方需要再说一次
一般作为一个容器,或多或少能够提供一定的web服务能力。比如Tomcat,其配置文件(tomcat-home\conf\web.xml)里面设定的默认servlet就是一个处理静态资源的servlet类。
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
由于容器和服务器的概念的确模糊,有时候都是混合用的。有人说Tomcat是servlet容器,也有说Tomcat是Web服务器,还有人说把Tomcat作为处理Servlet的引擎,其实都是有一定道理的。
Servlet的规范
servlet 是基于 Java 的 Web 组件,由容器进行管理,来生成动态内容。像其他基于 Java 的组件技术一样,servlet 也是基于平台无关的 Java 类格式,被编译为平台无关的字节码,可以被基于 Java 技术的 Web 服务器动态加载并运行。容器(Container),有时候也叫做 servlet 引擎,是 Web 服务器为支持 servlet 功能扩展的部分。客户端通过 servlet 容器实现的 request/response paradigm(请求/应答模式) 与 Servlet 进行交互。