- servlet
- genericServlet抽象类
- HttpServlet抽象类
- ServletRequest接口
- HttpServletRequest接口
- ServletResponse接口
- HttpServletResponse接口
- ServletConfig接口
- ServletContext接口
- javaweb应用的生命周期
- Servlet的生命周期
- servletcontext共享数据
- servletcontextlistener 监听器
- 防止页面被缓存
- 文件下载
- 上传文件
- 生成动态图像
- 读写Cookie
- 请求转发
- 包含
- 请求范围
- 重定向
- 避免并发问题
servlet
首先明确,下面各种方法和接口由容器提供商实现。我们自己只需要重写部分方法。
?
?
5个方法,三个方法由容器调用。
- init(ServletConfig config)初始化对象。
- service(ServletRequest req,ServletResponsr res)响应客户请求
- destroy() 结束Servlet对象
代码可调用下两个方法。
- getServletConfig() 返回一个包含Servlet初始化参数信息的对象
- getServletInfo 返回包含servlet创建者 版权的字符串
genericServlet抽象类
为Servlet接口提供了通用实现,与网络层协议无关,还实现了servletconfig接口和 serializable接口
service()是其中唯一的抽象方法。
该类使用了装饰设计模式
HttpServlet抽象类
针对不同的请求方式,实现了相应的方法。但是默认的实现,都会返回一个错误。
为什么是抽象类?
先要明确,有抽象方法一定是抽象类,但是抽象类不一定有抽象方法。二,只是强调该类在开发者没有自定义实现的情况下无法被初始化而已,这叫适配器 。可参考 里氏替换原则
ServletRequest接口
- 获得客户机信息
getRequestURL方法返回客户端发出请求时的完整URL。
getRequestURI方法返回请求行中的资源名部分。
getQueryString 方法返回请求行中的参数部分。
getRemoteAddr方法返回发出请求的客户机的IP地址
getRemoteHost方法返回发出请求的客户机的完整主机名
getRemotePort方法返回客户机所使用的网络端口号
getLocalAddr方法返回WEB服务器的IP地址。
getLocalName方法返回WEB服务器的主机名
getMethod得到客户机请求方式
- 获得客户机请求头
getHeader(string name)方法
getHeaders(String name)方法
getHeaderNames方法
- 获得客户机请求参数(客户端提交的数据)
getParameter(name)方法 一个数据时使用,多个数据时使用下一个GPV方法
getParameterValues(String name)方法
getParameterNames方法
getParameterMap方法
HttpServletRequest接口
getScheme()方法返回请求的计划,比如http,https或者ftp.
getServerName()方法返回被发送请求的服务器的主机名
getServerPort()方法返回被发送请求的端口号。
getContextPath()返回请求地址的根目录,以"/"开关,但不是以"/"结尾。
一个常用的获得服务器地址的连接字符串是:
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
getCookies() 取得cookie
getMethod() 取得请求方法,如get,post或put
getRequestURL() 取得请求URL(统一资源定位符)
getRequestURI() 取得请求URI(统一资源标识符)
getSession() 取得对应session
getQueryString() 取得请求中的查询字符串,即URL中?后面的内容
ServletResponse接口
setCharacterEncoding()设置响应正文的字符编码
setContenLength() 设置响应正文长度
setContentType() 设置响应正文MIME类型
setBufferSize() 设置响应正文的缓冲区的大小
reset()清空缓冲区内正文数据,并且清空响应状态代码和响应头
resetBuffer() 仅仅清空缓冲区正文数据,不清空状态代码和响应头
flushBuffer() 强制把缓冲区的响应正文发送到客户端
isCommitted() 判断数据是否已经发送到了客户端
getOutputStream() 返回一个对象,用来输出二进制的正文数据
getWriter() 返回一个对象,用它来输出字符串形式的正文数据
注:
- ServletResponse默认MIME类型是纯文本
HttpServletResponse默认MIME类型是html
编码方式:以下三种方式是等价的:
?
HttpServletResponse接口
addHeader(String name,String value)向HTTP响应头加入一项内容
sendError(int) 向客户端发一个代表错误的Http响应状态码
sendError(int src,String msg) 向客户端发一个错误代码和一个具体错误消息
addCookie(Cookie cookie) 向HTTP响应中加入一个cookie
ServletConfig接口
作为servlet接口init(ServletConfig config)的参数,与servlet产生关联。servletconfig对象与servletcontext对象关联。
- getInitParameter(String name) 根据给定的初始化参数名,返回匹配的初始化参数值(静态方法,可以直接调用,
String color = getInitParmeter("color")) - getInitParameterNames()返回一个枚举对象,包含所有的参数名
- getServletContext() (静态方法)返回当前servletContext对象
在web.xml中,
<init-param>
<param-name>color</param-name>
<param-value>red</param-value>
</init-param>
ServletContext接口
本接口是Servlet与Servlet容器之间直接通信的接口。容器在启动web应用的时候,会创建一个ServletContext对象,每个web应用都有唯一的对象。
- 在web应用范围内存取共享数据的方法。
- setAttribute(String name, java.lang.Object object)把一个对象和参数名绑定,存入到ServletContext中。
- getAttribute(String name) 返回相应的属性值
- etc
- 访问当前Web应用的资源
- getContentPath() 返回当前web应用的URL入口
- getInitParameter(String name) 根据给定参数名,返回初始化参数值(web.xml)中配置的
- 访问容器中的其他web应用
- 访问容器的相关信息
- 访问服务器端端文件系统资源
- 输出日志
- log(String msg) 向Servlet的日志文件中写日志
- log(String message,java.lang.Throwable throwable)向日志文件中写错误日志以及异常的堆栈信息
<context-param>
<param-name>email</param-name>
<param-value>[email protected]</param-value>
</context-param>
javaweb应用的生命周期
- 启动
把web.xml加载到内存
创建servletcontext对象
对所有的Filter初始化
对需要在启动时就被初始化的Servlet进行初始化(创建servletconfig对象,把web.xml中的初始化值包含进来,调用init(servletconfig对象) - 运行
如果servlet已经初始化了,就调用service()
如果还没被初始化,就先初始化,再调用service()方法 - 终止
销毁所有servlet
销毁Filter
销毁相关对象 如ServletContext
Servlet的生命周期
初始化阶段
1.容器加载Servlet类,将.class文件的数据读入到内存中
2.容器创建ServletConfig对象,容器使其与当前web应用的ServletContext对象关联
3.创建Servlet对象
4.调用servlet对象init(ServletConfig config)
下面两种情况,servlet会进入初始化
1.第一次被访问
2.web.xml配置了
运行阶段
销毁阶段
一个web应用一个context,一个servlet一个config,config可调用静态的getContext得到该对象
servletcontext共享数据
统计访问次数,就用serveletcontext设置一个属性(计数器)
servletcontextlistener 监听器
上述的统计方式会导致web应用重启后数据消失,由监听器监控web生命周期,当周期结束时,把计数写入数据库。
防止页面被缓存
设置响应头
?
第一种用于HTTP1.0的浏览器 第二种用于HTTP1.1 第三种都可以 表示过期时间
文件下载
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
OutputStream out; //输出响应正文的输出流
InputStream in; //读取本地文件的输入流
//获得filename请求参数
String filename=request.getParameter("filename");
if(filename==null){
out=response.getOutputStream();
out.write("Please input filename.".getBytes());
out.close();
return;
}
//创建读取本地文件的输入流
in= getServletContext().getResourceAsStream("/store/"+filename);
int length=in.available();
//设置响应正文的MIME类型
response.setContentType("application/force-download");
response.setHeader("Content-Length",String.valueOf(length));
response.setHeader("Content-Disposition", "attachment;filename=\""+filename +"\" ");
/** 把本地文件中的数据发送给客户 */
out=response.getOutputStream();
int bytesRead = 0;
byte[] buffer = new byte[512];
while ((bytesRead = in.read(buffer)) != -1){
out.write(buffer, 0, bytesRead);
}
in.close();
out.close();
}
}
上传文件
在html中,在
标签中,使用anctype="MULTIPART/FORM-DATA"
容器将表单包装成httpservletrequest对象,解析出复杂表单的个部分很复杂。所以利用Apache的两个软件包简化过程。
- commons-fileupload-1.2.1.jar 文件上传包
- commons-io-1.4.jar 输入输出包
主要是用fileupload上传文件,该包依赖于io包
?
对于每一个“multipart/form-data”的请求,该包把复合表单的每一部分看作一个FileItem对象。该对象分为两种:
1.formField 普通表单类型
2.非formFiled:上传文件类型,文件域就是这种类型。
DiskFileItemFactory类,对象的工厂
DiskFileItem类,把上传的文件数据保存到硬盘上
public class UploadServlet extends HttpServlet {
private String filePath; //存放上传文件的目录
private String tempFilePath; //存放临时文件的目录
public void init(ServletConfig config)throws ServletException {
super.init(config);
filePath=config.getInitParameter("filePath");
tempFilePath=config.getInitParameter("tempFilePath");
filePath=getServletContext().getRealPath(filePath);
tempFilePath=getServletContext().getRealPath(tempFilePath);
}
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/plain");
//向客户端发送响应正文
PrintWriter outNet=response.getWriter();
try{
//创建一个基于硬盘的FileItem工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置向硬盘写数据时所用的缓冲区的大小,此处为4K
factory.setSizeThreshold(4*1024);
//设置临时目录
factory.setRepository(new File(tempFilePath));
//创建一个文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);
//设置允许上传的文件的最大尺寸,此处为4M
upload.setSizeMax(4*1024*1024);
List /* FileItem */ items = upload.parseRequest(request);
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if(item.isFormField()) {
processFormField(item,outNet); //处理普通的表单域
}else{
processUploadedFile(item,outNet); //处理上传文件
}
}
outNet.close();
}catch(Exception e){
throw new ServletException(e);
}
}
private void processFormField(FileItem item,PrintWriter outNet){
String name = item.getFieldName();
String value = item.getString();
outNet.println(name+":"+value+"\r\n");
}
private void processUploadedFile(FileItem item,PrintWriter outNet)throws Exception{
String filename=item.getName();
int index=filename.lastIndexOf("\\");
filename=filename.substring(index+1,filename.length());
long fileSize=item.getSize();
if(filename.equals("") && fileSize==0)return;
File uploadedFile = new File(filePath+"/"+filename);
item.write(uploadedFile);
outNet.println(filename+" is saved.");
outNet.println("The size of " +filename+" is "+fileSize+"\r\n");
}
}
在web.xml中配置filepath和tempFilePath的值
生成动态图像
读写Cookie
cookie基本运行机制
?
有Cookie类,每个对象都包含一个名字和一个值
Cookie the Cookie = new Cookie("username", "tom")
response.addCookie(the Cookie)
Cookie cookies[] = request.getCookies()
cookie.setMaxge(int)
大于零,表示在客户端硬盘保存的时间
等于零,删除当前cookie
小于零,就不保存到硬盘,仅仅保存在当前浏览器进程中,关闭浏览器,cookie消失
默认为-1
请求转发
一个Servlet对象无法获得另一个servlet对象的引用。
特点:
* 源组件和目标组件处理的都是同一个客户请求,共享一个request和response对象 源组件的响应结果不会发送到客户端(会给目标组件再发送到客户端)
* 如果在请求转发前提交了响应结果(如flushBuffer,close),会抛出异常(会显示源组件内容,日志中会有异常)。如果不提交,不会显示。
* disspatcher.forward(request,response)后的代码也会执行
* 目标组件可以是servlet、jsp、html
* 都依赖于javax.servlet.RequestDispatcher()
这个接口表示请求分发器:
forward() 把自己的东西转发到别的组件
include() 将别人的东西包含到自己中
通过两种方式得到RequestDispathcer对象
* 调用servletcontext的getrequestDispatcher(String path)
* 调用servletRequset的getrequestdispatcher(String path)
path为目标组件的路径。两种区别在于,前者要求绝对路径,后者可以为绝对路径也可以是相对路径
包含
特点:
源组件和被包含的目标组件的输出数据都会添加到响应结果
目标组件中对响应状态代码或者响应头的操作都会被忽略。
请求范围
web的应用范围是指web应用的生命周期,与servletcontext对象的生命周期对应。
请求范围是指服务器端响应一次客户请求的过程,从一个servlet接受到请求开始,到返回响应结果结束。与servletrequest对象和servletresponse对象的生命周期对应。
重定向
Response.sendRedirect(String location)
1.用户输入特定的URL,请求访问服务器端的某个组件
2.服务器端组件返回一个302的响应结果,含义为让浏览器再请求访问另外一个web组件,在响应结果中包含另外一个web组件的URL,这个组件不一定在同一个服务器上
3.浏览器接受到这种响应结果后,立即自动请求访问另一个组件
4.浏览器收到另一个组件的响应结果
源组件
request.setAttribute("msg",message)
response.sendRedirect("/helloapp/output1?msg="+message)
output1组件:
String message = (String)request.getAttribute("msg")
注意:
- 源组件的响应结果不会发送到浏览器中,如果在sernRedirect之前已经提交了响应结果,会抛出异常。
- 源组件调用重定向方法后到代码也会执行
- 不共享数据(request或者response)
避免并发问题
多个客户访问同一个servlet,容器会创建多个线程来访问同一个对象,而不是为每个访问创建一个对象。这样就会导致一些并发问题。
解决方案:
1. 合理决定在Servlet中定义的变量的作用域
2. 对于多线程同时访问数据导致的并发问题的情况,使用同步机制进行同步
3. 不要使用被废弃的 javax.servlet.singthreadmodel
对于方案1:
一个请求对应一个工作线程,一个线程对应一个username变量。
局部变量在一个方法中定义。当一个线程执行这个方法时就会创建这个局部变量。如果有多个线程执行该方法,每个线程都会有自己的局部变量。
实例变量在类中定义,每个实例都有自己的实例变量。如果多个线程执行一个实例方法,都会访问同一个实例变量。
容器对于每一个servlet只会创建一个servlet对象。
不要使用实例变量,要把变量定义在service方法中
对于方案2:
使用synchronise()同步代码块,避免同时操作同一个数据。
对于方案3:
如果servlet实现了这个接口,会有两种方式来运行servlet
1.任意时刻,只允许一个工作线程执行servlet的service方法。
2.创建一个servlet对象池,不同的客户操作不同的servlet对象。
Tomcat是第二种方式。