JavaWEB Servlet和JSP
1、JSP和Servlet
JSP页面会被转换和编译成Servlet程序。
Servlet应用无法独立运行,必须运行在Servlet容器中。
Web服务器和Web客户端间通过HTTP协议通信,因此Web服务器也叫HTTP服务器
Apache Tomcat和Jetty是当前最流行的Servlet/JSP容器,并且它们是免费而且开源的、
Servlet和JSP只是Java企业版中众多技术中的两个,其他Java EE技术还有Java消息服务,企业Java对象、JavaServer Faces以及Java持久化等
要运行Java EE应用,需要一个Java EE容器,例如GlassFish、JBoss、Oracle Weblogic或者IBMWebSphere。诚然,我们可以将一个Servlet/JSP应用部署到一个Java EE容器上,但一个Servlet/JSP容器就已经满足需要了,并且更加轻量。当然,Tomcat和Jetty不是Java EE容器,因此无法运行EJB或JMS技术。
2、HTTP协议
HTTP请求
方法-URI-协议/版本
请求头信息
请求正文
HTTP响应
协议—状态码—描述
响应头信息
响应正文
3、ServletAPI
Servlet3.0或更高版本不需要web.xml文件,直接使用注解就可以
Servlet API有以下4个Java包:
javax.servlet,其中包含定义Servlet和Servlet容器之间契约的类和接口。
javax.servlet.http,其中包含定义HTTP Servlet和Servlet容器之间契约的类和接口。
javax.servlet.annotation,其中包含标注Servlet、Filter、Listener的标注。它还为被标注元件定义元数据。
javax.servlet.descriptor,其中包含提供程序化登录web应用程序的配置信息的类型。
3.1、javax.servlet包中的主要类
Servlet,ServletRequest,ServletResponse,ServletContext,ServletConfig,RequestDispatcher,Filter
注意线程安全性。Servlet实例会被一个应用程序中的所有用户共享,因此不建议使用类级变量,除非它们是只读的,或者是java.util.concurrent.atomic包的成员。
如果想让某一个资源可以被Servlet访问,但不可以被用户访问,那么就要把它放在WEB-INF目录下
3.1.1 、Servlet常用方法:
int getContentLength()
String getContentType()
String getParameter(String name) 通常用于返回HTML表单域的值,也可以用于获取查询字符串的值
String getProtocol()
除了getParameter外,还可以使用getParameterNames、getParameterMap和getParameterValues获取表单域名、值以及查询字符串。
3.1.2 、ServletResponse常用方法:
setContentType(String type)
getWriter() 字符串数据 (常用)
getOutputStream() 二进制数据
3.1.3、ServletConfig常用方法:
getInitParameter(String name)
getInitParameterNames()
getServletContext()
@WebServlet(name = "ServletConfigDemoServlet",
urlPatterns = { "/servletConfigDemo" },
initParams = {
@WebInitParam(name="admin", value="Harry Taciak"),
@WebInitParam(name="email", value="admin@example.com")
}
)
3.1.4、ServletContext:
ServletContext表示Servlet应用程序。每个Web应用程序只有一个上下文。在将一个应用程序同时部署到多个容器的分布式环境中,每台Java虚拟机上的Web应用都会有一个ServletContext对象。
通过在ServletConfig中调用getServletContext方法,可以获得ServletContext
3.1.5、GenericServlet:
GenericServlet实现了Servlet和ServletConfig接口,并完成以下任务:
- 将init方法中的ServletConfig赋给一个类级变量,以便可以通过调用getServletConfig获取。仅当该servlet第一次被用户访问时调用
- 为Servlet接口中的所有方法提供默认的实现。
- 提供方法,包围ServletConfig中的方法。
常用方法:
init(ServletConfig config)
init()
继承GenericServlet的类,只实现service方法即可。而实现Servlet接口要写 init,service ,getServletConfig,getServletInfo等方法;
3.2、javax.servlet.http中的主要类
HttpServlet,HttpServletRequest,HttpServletResponse
3.2.1、HttpServlet 常方法:
void service(HttpServletRequest request,HttpServletResponse response)
使用时不必覆盖service方法,直接覆盖以下方法即可
doGet、doPost、doHead、doPut、doTrace、doOptions和doDelete。
3.2.2、HttpServletRequest常用方法:
String getContextPath()
Cookie[] getCookies()
String getHeader(java.lang.String name)
String getMethod()
String getQueryString()
HttpSession getSession()
3.2.3、HttpServletResponse常用方法:
void addCookie(Cookie cookie)
void addHeader(String name, String value)
void sendRedirect(String location)
3.2.4、表单处理
空的输入域或者文本区会发送空的字符串。因此,有输入域名称的,ServletRequest.getParameter绝对不会返回null。
select多选发出一个字符串数组,并且必须通过SelectRequest.getParameterValues进行处理。
复选框比较奇特,选中的发送on到服务器,未选中的复选框则不向服务器发送任何内容,ServletRequest.getParameter(fieldName)返回null。
单选框将被选中按钮的值发送到服务器,未选中的则不向服务器发送任何内容,ServletRequest.getParameter(fieldName)返回null。
多个输入同名的元素,ServletRequest.getParameterValues来获取它们。
3.2.5、web.xml
使用部署描述符有诸多好处
@WebServlet中没有对等元素的元素,如load-on-startup元素
如果需要修改配置值,如Servlet路径,则不需要重新编译Servlet类。
可以将初始参数传给一个Servlet,并且不需要重新编译Servlet类
总结
Servlet技术是Java EE技术的一部分。所有Servlet都运行在Servlet容器中,容器和Servlet间的接口为javax.servlet.Servlet。javax.servlet包还提供了一个名为GenericServlet的Servlet实现类,该类是一个辅助类,以便可以方便的创建一个servlet。不过,大部分servlet都运行在HTTP环境中,因此派生一个javax.servlet.http.HttpServlet的子类更为有用,注意HttpServlet也是GenericServlet的子类。
4、会话管理
4种不同的状态保持技术:URL重写、隐藏域、cookies和HTTPSession对象。
URL重写是一种会话跟踪技术,它将一个或多个token添加到URL的查询字符串中。
隐藏域,放到HTML表单的隐藏域中。当表单提交时,隐藏域的值也同时提交到服务器端。隐藏域技术仅当网页有表单时有效。
URL重写和隐藏域仅适合保存无须跨越太多页面的信息
Cookies:
读取浏览器提交的cookie,可以通过HttpServletRequest接口的getCookies方法
读取单个cookie ,你需要遍历整个数组来查询某个特定名称的cookie,不支持按名称获取单个cookie
删除一个cookie,你只能创建一个同名的cookie,并将maxAge属性设置为0
HttpSession:
放到HttpSession的值不限于String类型,可以是任意实现java.io.Serializable的java对象。
HttpSession.还定义了一个名为invalidate 的方法。该方法强制会话过期,并清空其保存的对象。
HttpSession 会在用户不活动一段时间后自动过期,该时间可以通过部署描述符的 session-timeout元素配置,若设置为30,则会话对象会在用户最后一次访问30分钟后过期,如果部署描述符没有配置,则该值取决于Servlet容器的设定。大部分情况下,你应该主动销毁无用的HttpSession,以便释放相应的内存。
5、JavaServer Pages(JSP)
Servlet有两个缺点是无法克服的:
首先,写在Servlet中的所有HTML标签必须包含Java字符串,这使得处理HTTP响应报文的工作十分烦琐;
第二,所有的文本和HTML标记是硬编码,导致即使是表现层的微小变化,如改变背景颜色,也需要重新编译。
JavaServer Pages(JSP)解决了上述两个问题。同时,JSP不会取代Servlet,相反,它们具有互补性。现代的Java Web应用会同时使用Servlet和JSP页面。
5.1、JSP页面本质上是一个Servlet。
当一个JSP页面第一次被请求时,Servlet/JSP容器
主要做以下两件事情:
(1)转换JSP页面到JSP页面实现类,该实现类是一个实现javax.servlet.jsp.JspPage接口或子接口javax.servlet.jsp.HttpJspPage的Java类。
JspPage是javax.servlet.Servlet的子接口,这使得每一个JSP页面都是一个Servlet。
该实现类的类名由Servlet/JSP容器生成。如果出现转换错误,则相关错误信息将被发送到客户端。
(2)如果转换成功,Servlet/JSP容器随后编译该Servlet类,并装载和实例化该类,像其他正常的Servlet一样执行生命周期操作。
对于同一个JSP页面的后续请求,Servlet/JSP容器会先检查JSP页面是否被修改过。如果是,则该JSP页面会被重新翻译、编译并执行.
5.2、JSP自带的API包
javax.servlet.jsp。其中的两个重要成员是JspPage和HttpJspPage接口。
javax.servlet.jsp.tagext。包括用于开发自定义标签的类型。
javax.el。提供了统一表达式语言的API。
javax.servlet.jsp.el。提供了一组必须由Servlet/JSP容器支持
welcome.jsp页面在第一次请求时被翻译成名为welcome_jsp的Servlet。你可以在Tomcat工作目录下的子目录中找到生成的Servlet,该Servlet继承自org.apache.jasper. runtime.HttpJspBase,这是一个抽象类,继承自javax.servlet.http.HttpServlet并实现了javax.servlet.jsp.HttpJspPage。
添加新的JSP界面后,无须重启Tomcat。
5.3、JSP隐式对象
request javax.servlet.http.HttpServletRequest
response javax.servlet.http.HttpServletResponse
out javax.servlet.jsp.JspWriter
session javax.servlet.http.HttpSession
application javax.servlet.ServletContext
config javax.servlet.ServletConfig
pageContext javax.servlet.jsp.PageContext
page javax.servlet.jsp.HttpJspPage
exception java.lang.Throwable
pageContext 属性值可被存储在4个范围之一:页面、请求、会话和应用程序。PAGE_SCOPE、REQUEST_ SCOPE、SESSION_SCOPE和APPLICATION_SCOPE
pageContext 它包含了所有其他的JSP隐式对象
5.4、JSP指令
page和inclue
<%@ page attribute1="value1" attribute2="value2" ... %>
属性取值 import、session、errorPage、isErrorPage、contentType、pageEncoding、extends、language、buffert等
<%@ include file="copyright.jspf"%>
以JSPF为扩展名的文件代表JSP fragement。虽然JSP fragement现在被称为JSP segment,但为保证一致性,JSPF后缀名依然被保留。
区分include动作,include动生是生成html后,再包含到当前界面,可以传递参数。而include指令是内容包括后再转化为html的,不能传递参数。
<jsp:include page="jspf/menu.jsp">
<jsp:param name="text" value="How are you?"/>
</jsp:include>
技巧:建议视图界面放到jsp目录,可被引用的文件放到jspf目录
5.5、脚本元素
<%符号开始,以%>符号结束
是一个Java代码块
表达式以“<%=”开始,并以“%>”结束,功能是使用隐式对象out的打印方法输出结果
Today is <%=java.util.Calendar.getInstance().getTime()%>
等于
Today is
<%
out.print(java.util.Calendar.getInstance().getTime());
%>
声明以“<%!”开始,并以“%>”结束
<%!
public String getTodaysDate() {
return new java.util.Date();
}
%>
<html>
<head><title>Declarations</title></head>
<body>
Today is <%=getTodaysDate()%>
</body>
</html>
6、表达式语言
EL表达式以 ${ 开头,并以 } 结束
EL表达式可以利用[ ]或者.运算符来访问该属性。“[ ]”和“.”运算符类似;“[ ]”是比较规范的形式,“.”运算符则比较快捷。
推荐[] 如accept-language不是一个合法的Java变量名。如果用“.”运算符访问它,将会导致异常。(字母数字下画线,不能以数字开头,这里有个中画线)
访问对像时,不需要写get和set ,直接写类中的私有变量就可以。
EL中的隐式对象:
initParam用于获取上下文参数的值
param用于获取请求参数值
paramValues可以获取一个请求参数的多个值象header表示一个包含所有请求标题的Map
cookie可以用来获取一个cookie
applicationScope用于获取应用程序范围级变量的值
隐式对象sessionScope、requestScope和pageScope与applicationScope相似。但是,其范围分别为session、request和page。
7、JSTL
JavaServer Pages Standard Tag Librar
JSTL是一个定制标签库的集合,用来解决像遍历Map或集合、条件测试、XML处理,甚至数据库访问和数据操作等常见的问题。
可以说是EL表达式的补充
区域子函数URI 前缀
核心 http://java.sun.com/jsp/jstl/core 前缀 c
XML http://java.sun.com/jsp/jstl/xml 前缀 x
国际化 http://java.sun.com/jsp/jstl/fmt 前缀 fmt
数据库SQL http://java.sun.com/jsp/jstl/sql 前缀 sql
函数 http://java.sun.com/jsp/jstl/functions 前缀 fn
在JSP页面中使用JSTL库,必须通过以下格式使用taglib指令:
<%@ taglib uri="uri" prefix="prefix" %>
8、自定义标签
javax.servlet.jsp.tagext
8.1、实现SimpleTag接口或者扩展SimpleTagSupporta的标签
自定义标签的实现,叫作标签处理器,而简单标签处理器是指继承SimpleTag实现的标签处理器。
注意简单标签处理器必需有无参数构造器
JSP容器通过setJspContext的方法,传入JspContext对象:该对象中最重要的方法是getOut,它能返回JspWriter,通过JspWriter就可以把响应返回前端了。
如果自定义标签被另一个自定义标签所嵌套,JSP容器就会调用setParent的方法。
注意在构建标签处理器时,需要在构建目录中有Servlet API及JSP API。
在标签处理器能够被JSP页面使用之前,它需要在标签库描述器中注册一下,这个描述器是以.tld结尾的XML文件,这个文件必须放在WEB-INF目录下。
使用标签
<%@ taglib uri="/WEB-INF/mytags.tld" prefix="easy"%>
实现SimpleTag接口或者扩展SimpleTagSupporta的标签处理器都可以有属性。
8.2、编写EL函数
含静态方法的public类,每个类的静态方法表示一个EL函数。
用function节点在标签库描述器中注册这个函数。
<%@ taglib uri="/WEB-INF/functiontags.tld" prefix="f"%>
标签文件tagfile
从JSP 2.0开始,通过tag file的方式,无须编写标签处理类和标签库描述文件,也能够自定义标签了。tagfile在使用之前无须编译,并且不需要标签库定义文件。
一个tag file以tag和tagx为后缀,它们可以包含其他资源文件。一个被其他文件包含的tag file应该以tagf为后缀。
tag文件必须放在应用路径的WEB-INF/tags目录下才能生效。和标签处理类一样,tag 文件可以被打到jar包里。
9、监听器Lisenter
监听器接口可以分为三类:ServletContext、HttpSession 和ServletRequest 。
两种注册方式
@WebListener
public class ListenerClass implements ListenerInterface {
}
或
</listener>
<listener-class>fully-qualified listener class</listener-class>
</listener>
10、过滤器Filters
Filter是拦截Request请求的对象:在用户的请求访问资源前处理ServletRequest以及ServletResponse,它可用于日志记录、加解密、Session检查、图像文件保护等。
主要方法:init、doFilter、destroy
两种注册方式
@WebFilter(filterName="DataCompressionFilter", urlPatterns={"/*"})
或
<filter>
<filter-name>DataCompressionFilter</filter-name>
<filter-class>the fully-qualified name of the filter class</filter-class>
</filter>
<filter-mapping>
<filter-name>DataCompresionFilter</filter-name>
<url-pattern>/ *</url-pattern>
</filter-mapping>
11、解决servlet线程安全问题
1. Automic家族
2.线程安全集合
3.单线程Executor的队列
12、修饰Requests及Responses
Decorator模式或者Wrapper模式
Decorator类及被修饰对象的类需要实现相同的接口
ServletRequestWrapper、ServletResponseWrapper以及HttpServletRequestWrapper、HttpServletResponseWrapper。
通过继承Wrapper,只需要实现你需要变更的方法就可以了。如果不用Wrapper,则需要继承Request/Response并实现Request/Response中所有的方法
可实现 可重复读ServletInputStream, 参数过滤等。
13、异步Servlet
在Tomcat 7中,处理传入的请求的最大线程数是 200
这个异步支持只适合你有一个长时间运行的任务并且要把运行结果通知给用户。如果你只有一个长期运行任务,但用户并不需要知道处理结果,那么你可以提交一个Runnable给Executor(执行器)并立即返回
14、ServletContainerInitializer
在Servlet3.0中为我们提供了一个接口:javax.servlet.ServletContainerInitializer。我们可以在这个接口的实现类中进行Servlet、Filter等信息的配置。它的作用和web.xml是很像,可以用来取代web.xml。
//添加Servlet
ServletRegistration.Dynamic dynamicServlet = servletContext.addServlet("demoServlet", new DemoStartServlet());
//请求路径
dynamicServlet.addMapping("/demo");
//Servlet InitParam
dynamicServlet.setInitParameter("demo", "demo");
dynamicServlet.setLoadOnStartup(1);
//添加过滤器
FilterRegistration.Dynamic dynamicFilter = servletContext.addFilter("filter", new DemoFilter());
dynamicFilter.addMappingForServletNames(EnumSet.of(DispatcherType.REQUEST), true, "demoServlet");