文章目录
前言
1、Servlet
1.1servlet的运行原理
在浏览器地址栏中输入一个网址并确认,浏览器会向服务器发送一个HTTP请求,服务器端接收这个请求,并对请求作相应的回应处理。并将回应处理结果返回给浏览器,浏览器再把回应的内容显示出来,这种基于请求-响应的模式就是典型的web应用程序的访问过程。
我们知道:在计算机网络中,两台主机之间数据的交互需要遵循一系列的应用层,传输层,网络层,链路层以及物理层的相关协议。HTTP协议作为一种应用层协议 ,规定了因特网上数据传送的规则,因此要实现两台主机之间的交互,两台主机都必须实现该协议。
而使用较多的免费版的Tomcat就可以看作是实现了服务器端HTTP协议的应用,而浏览器则是实现了客户端HTTP协议的应用。在Java Web项目中,服务器端具体的响应处理则是通过Servlet实现。
servlet是什么?从程序语言的角度看,Servlet是实现了javax.servlet.Servlet接口的类,该接口针对不同HTTP请求规定了特定的方法来处理,只要实现了这些方法,客户端访问web程序的时候,web服务器就会调用相应方法完成业务处理(具体怎么被调用,这就需要对web服务器的比较了解,暂且搁置)。在实际应用中,容器中的servlet.api.jar文件提供了对Servlet的支持,该文件中包含两个常用包:javax.servlet.*和javax.servlet.http.*包javax.servlet.http.HttpServlet类就实现了Servlet接口的所有方法,可以直接继承该类重写需要的方法,从而提供编程效率。
在java web程序中,servlet的基本配置如下:
web.xml文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<--为servlet命名-->
<servlet>
<servlet-name>uploadServlet</servlet-name>
<servlet-class>com.xin.servlet.UploadServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<--为servlet定制URL-->
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/src/com/xin/servlet/*</url-pattern>
</servlet-mapping>
<--指定欢迎页(客户端请求时的首页)}-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
<servlet’>配置相应的Servlet类;<servlet-mapping’>配置servlet的映射路径,也即是客户端相应路径的请求才会交给该servlet处理;servlet程序要被web服务器正常调用,必须继承相应的类,例如HttpServlet;应用中通常是扩展HttpServlet类,该类声明了一些处理客户端请求的对应方法,根据客户端的请求类型(POST、GET、HEAD等),编写相应的处理方法,在博主的小项目中,客户端使用的是post请求提交表单,因此在servlet中的编写了方法:doPost();servlet的规范规定了对应客户端的请求方法必须是do+请求类型(例如doGet);
1.2 Servlet生命周期
servlet会在服务器启动或者说是第一次请求servlet的时候开始生命周期,在服务关闭的时候结束生命周期,也就是说在整个应用进程中始终只存在一个servlet实例,当多个客户端并发请求时,服务器会启动多个线程来执行servlet中的service方法,而Servlet中的init()与destroy()方法只会分别在Servlet被加载和销毁时执行一次。
1.3 Servlet的线程安全性
通过上文中对servlet生命周期的描述可以知道,servlet不是线程安全的,如果在自定义的Servlet中定义属性字段,在被多个线程写入时会出现数据不同步的问题,从而引发线程安全问题。
2 Filter过滤器
2.1过滤器的原理
过滤器是servlet规范中的两个高级特性之一,如图所示,过滤器是在servlet外对请求和响应的处理,多个过滤器形成滤镜链,客户端请求在抵达servlet之前会经过滤镜链中的每个过滤器,同样服务器的响应在抵达客户端之前也会经过滤镜链中的所有过滤器。
2.2过滤器的使用
同servlet一样,一个过滤器必须实现一个接口:javax.servlet.Filter,该接口定义了三个方法,其中init()方法与destroy()仅被调用一次,doFilter()方法在每次有请求时都会被调用:
public interface Filter {
//web程序启动时调用此方法,用于初始化过滤器
public void init(FilterConfig filterConfig) throws ServletException;
//web请求服务器时经过该方法,chain滤镜链,将请求传给下个Filter或者servlet
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
//web程序关闭时调用池方法,用于销毁一些资源
public void destroy();
}
在使用过滤器时,在web.xml的配置如下:
<!-- 配置过滤器名称,对应类,以及初始化参数-->
<filter>
<filter-name>myfilter</filter-name>
<filter-class>filter.MyFilter</filter-class>
<init-param>
<param-name>paramName</param-name>
<param-value>parameValue</param-value>
</init-param>
<init-param>
<param-name>paramName2</param-name>
<param-value>parameValue2</param-value>
</init-param>
</filter>
<!-- 配置什么规则下使用过滤器
<dispatcher>配置到达servlet的方式:
REQUEST:表示仅当直接请求Servlet时才生效
FORWARD:仅当某servlet通过FORWARD到该servlet时才生效
INCLUDE:JSP中可以通过动作标识<jsp:include/>请求某个servlet,仅在这种情况下有效
ERROR:JSP中可以通过指令<%@ page errorPage="error.jsp"%>指定错误处理页面,只有在这种情况下有效;
-->
<filter-mapping>
<filter-name>myfilter</filter-name>
<url-pattern>/jsp/*</url-pattern>
<url-pattern>*.do</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
3 监听器
3.1监听器的原理
Listener监听器是Servlet规范的另一个高级特性,用来监听java web程序中的事件,Servlet规范中定义了三类监听器:
- 监听对象的创建与销毁:HttpSessionListener、ServletContextListener、ServletRequestListener用于监控session、context、request的创建与销毁;
- 监听对象属性的变化:HttpSessionAttributeListener、ServletContextAttributeListener、ServletRequestAttributeListener,当向被监听对象中添加、更新、移除属性时;
- 监听Session内的对象:HttpSessionBindingListener、HttpSessionActivationListener对象被放进移出session或者是启动、关闭服务器会触发相应的事件;
3.2监听器的使用
以上8种类型的监听器分别对应不同的事件,当希望某事件发生时程序进行相关处理,可以实现对应的监听器接口,重写相应的方法。与servlet和Filter的使用不同,listener只需进行如下配置:
<listener>
<listener-class>myListener</listener-class>
</listener>
以下是监听器使用的一个小例子:
package listener;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
public class MyListener implements HttpSessionActivationListener{
//session中添加属性时调用的方法
public void attributeAdded(HttpSessionBindingEvent event ){
//获取添加的属性名
String name=event.getName();
//.......todo...................
}
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
}
监听器与事件总是结对的,具体的使用可以查阅API,本文主要讲解基本的使用规范。
4 Session
session可以说是由web服务器生成和管理的,用户初次访问服务器时会被创建,可以通过以下方式 获取到session对象: request.getSession()
Session虽然保存在服务器中,对客户端浏览器来说貌似无要求,但是由于HTTP是一种无状态协议(简单来讲就是同一客户请求同一应用的不同URL时不能被服务器知晓是一个用户所为),不能够依据HTTP连接来判断是不是为同一用户,而Session的作用就是为了实现用户的浏览追踪,那么如何实现呢?
在用户初次请求服务时,服务器自动向浏览器发送一个名为JSESSIONId的cookie,其值为session的id,可以通过HttpSession的getId()获取。
5 Cookie
Cookie相当于是用户初次访问服务器时,服务器为用户颁发的通行证,客户端保存这个通行证,在二次访问中,客户端会将请求的网址连同这个通行证一起提交给服务器,服务器就可以检查该Cookie来辨别用户。在Seesion中,用户首次请求服务后被颁发一个会话ID(SessionID),作为其后面继续访问的服务的跟踪ID,服务器可以借此判断用户身份,添加或修改关于用户的相关信息。
注意只有该会话ID是跟整个会话绑定的,不会随着页面的跳转而丢失,但是一般的cookie会随着页面的跳转而丢失
以下是关于一个文件上传的小demo,主要通过该demo演示servlet处理请求的过程以及cookie与session的基本工作原理。
index页面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>首页</title>
</head>
<body>
<div style="width:100%;hight:50px;margin-bottom: 10px;background-color: blue;color: yellow" align="center">文件上传功能测试</div>
<div style="width:100%;">
<div style="width:50%;height:100px"align="left">
<form action="<%= request.getContextPath() %>/src/com/xin/servlet" method="post" enctype="multipart/form-data">
文件上传:<input type="file" name="file1" /></br>
文本域:<input type="text" name="description1"/>
<button type="submit">提交</button>
</form>
</div>
<div style="width:50%;height:300px">
<div style="margin-top: 20px;margin-bottom: 20px">服务器返回上传时间,保存在Session中的特性:<%=session.getAttribute("uploadTime") %></div>
<div style="margin-bottom: 20px">服务器返回输入的文本域信息,保存在Session中的特性:<%=session.getAttribute("文本域") %></div>
<div>服务器返回上传文件名称,保存在Cookie中的属性:</br>
<%
Cookie[] cookies=request.getCookies();
if(cookies!=null&&cookies.length!=0){
for(Cookie cookie:cookies){
%>
<p>Cookie的名称:<%=cookie.getName() %></p>
<p>Cookie的取值:<%=cookie.getValue() %></p>
<% } }%>
</div>
</div>
</div>
</body>
</html>
servlet处理请求:`
package com.xin.servlet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
*
* @author XueXin
*
*/
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* 处理页面的post请求
*/
@Override
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException{
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if(isMultipart) {
DiskFileItemFactory fac = new DiskFileItemFactory();
//创建一个新的文件上传处理程序
ServletFileUpload upload =new ServletFileUpload (fac);
//获取会话对象,
HttpSession session=request.getSession();
//List<HttpSession> sessions=new LinkedList<HttpSession>();
//会话跟踪的演示
session.setAttribute("uploadTime",new Date());
String description1=null;
try {
//解析浏览器请求,
List <FileItem> list = upload.parseRequest(request);
for (Iterator<FileItem> iterator = list.iterator(); iterator.hasNext();) {
FileItem fileItem = (FileItem) iterator.next();
//如果是文本域
if (fileItem.isFormField()&&"description1".equals(fileItem.getFieldName())){
description1=new String(fileItem.getString().getBytes("ISO-8859-1"), "UTF-8");
session.setAttribute("文本域",description1);
}else if("file1".equals(fileItem.getFieldName())){
//如果是文档文件
File file1=exportData(fileItem);
//添加cookie,验证在客户端获取到cookie;
Cookie cookie=new Cookie("fileName", file1.getName());
response.addCookie(cookie);
}
}
//请求转发,转发到不同的页面看看,session的追踪效果;
RequestDispatcher dispatcher=request.getRequestDispatcher("/WEB-INF/views/welcome.jsp");
dispatcher.forward(request, response);
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
/**
*@function 读取上传文件并保存在本地,IO模型的解析;
*/
public File exportData(FileItem fileItem) throws IOException {
//存储下载文件
File downLoadFile=new File(new String(fileItem.getName().getBytes("UTF-8")),"UTF-8");
//下载文件的存储位置
File file1=new File(this.getServletContext().getRealPath("exportData"),downLoadFile.getName());
//创建目录
file1.getParentFile().mkdirs();
//创建文件
file1.createNewFile();
InputStream inputStream=fileItem.getInputStream();
OutputStream outputStream=new FileOutputStream(file1);
try {
byte[] buffer=new byte[1024];
int len=0;
//从输入流读取数据
while((len=inputStream.read(buffer))>-1) {
//向输出流写入数据
outputStream.write(buffer, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
outputStream.close();
inputStream.close();
}
return file1;
}
}
跳转页面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>转发页面</title>
</head>
<body>
<div style="margin-top: 20px;margin-bottom: 20px">服务器返回上传时间,保存在Session中的时间信息:<%=session.getAttribute("uploadTime") %></div>
<div style="margin-bottom: 20px">服务器返回输入的文本域信息,保存在Session中的文本域信息:<%=session.getAttribute("文本域") %></div>
<div>服务器返回上传文件名称,保存在Cookie中的属性:</br>
<%
Cookie[] cookies=request.getCookies();
if(cookies.length!=0){
for(Cookie cookie:cookies){
%>
<p>Cookie的名称:<%=cookie.getName() %></p>
<p>Cookie的取值:<%=cookie.getValue() %></p>
<% } }%>
</div>
</body>
</html>
图片是关于项目运行之后的具体前台页面效果,在demo中,用户首次访问服务时,并没有经过servlet的处理,但是服务器自动为用户颁发了一个会话跟踪ID也即是JSESSIONID;
上传文件b.txt,在文本域中输入message1提交之后,经过servlet中doPost()方法的处理,session中添加了message1属性值和文件上传时间,并且将文件名和值保存在cookie中: