Sun提供的一种动态web资源开发技术,本质上就是一段java
小程序。
可以将Servlet
加入到Servlet
容器中运行。
tomcat
既是web
容器也是Servlet
容器。
先写一个类,实现sun公司定义的Servlet
接口:
package java_web.Servlet;//这里是我自己定义的包,后面用到
import java.io.*;
import javax.servlet.*; //servlet-api.jar中的包
//GenericServlet类已经帮我们实现了Servlet的接口,只需继承并编写service即可
public class HelloServlet extends GenericServlet{
public void service(ServletRequest req, ServletResponse res) {
res.getWriter().write("hello Servlet");
}
}
上面是参照javaee api
文档写的一个简单Servlet
,然后在同目录下运行指令:
javac HelloServlet.java
会报错,找不到那些关于Servlet
的包,因为我们一般使用的是JavaSE
,缺少这些必要的package
,而tomcat
实际上是自带有的,所以我们可以通过命令设置临时的classpath
,以便编译:
set classpath=%classpath%;%CATALINA_HOME%\lib\servlet-api.jar;
再运行javac HelloServlet.java
,这时就应该可以成功了,不过我们需要的是一个文件夹package
而不是单独的class
文件,所以应该使用指令:
javac -d . HelloServlet.java
会在当前目录下生成一个包含了HelloServlet.class
的package
,名为java_web
。
接下来,将该整个包文件放到Tomcat中:
tomcat\webapps\news\WEB-INF\classes\java_web
news是一个web应用,需要将刚创建的整个包文件java_web放到该应用的classes中。
还需要配置WEB-INF
中的web.xml
(可以参考conf
中的web.xml
):
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>HelloServletservlet-name>
<servlet-class>java_web.Servlet.HelloServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>HelloServletservlet-name>
<url-pattern>/servlet/firsturl-pattern>
servlet-mapping>
web-app>
这样就通过URL映射到了相应的类文件。
在浏览器地址栏中访问localhost/news/servlet/first
,即可看到刚才创建的servlet
文件效果。
点击File
→ new
→ project
:
选择Java Enterprise
,并在右边配置好版本和路径,点击Next
,再勾选Create project from template
(或者在上一步勾选 Web Application
):
点击Next
,选择项目的名称和目录,设置服务器为tomcat
:
点击finish
后,项目创建完毕,用默认的配置直接启动Tomcat
运行该项目,会生成out
文件夹及其内部结构,并自动打开浏览器跳转到index.jsp
。
src
目录存放的是一些待编译的java
文件,web
目录存放的是网页的web资源。out
目录存放的是编译输出文件,其中web_war_exploded
的名称意为,项目名称为web
的应用资源经过打包的war
文件展开后的结构。
为什么会出现这个呢?其实当我们启动项目,IDEA会自动生成这样的war
文件,部署运行在Tomcat
服务器上,只是为了方便所以开启了exploded
模式,展开显示给开发者,可以在Project Structure
中修改为war
模式。因此,某个module
有了artifacts
就可以部署到应用服务器中了。
官方对artifacts
的解释:
An artifact is an assembly of your project assets that you put together to test, deploy or distribute your software solution or its part. Examples are a collection of compiled Java classes or a Java application packaged in a Java archive, a Web application as a directory structure or a Web application archive, etc.
artifact
是一个项目里资源的整合,比如说一些Java
类文件,web
资源等,我们可以对它们进行测试、部署或发布。
整个过程看似顺理成章,但是别忘了,我们是用IDEA生成的项目默认配置启动的,虽然运行成功,我们却不知其所以然。下面对它的一些相关配置进行解释。
首先要明确,我们有两块需要配置:Project Structure
和 Run/Debug Configurations
,前者指的是刚创建的这个web项目的结构相关配置,后者指的是我们运行这个项目所用的相关配置,即Tomcat
服务器的配置。
Project Struture
:理解 IntelliJ IDEA 的项目配置和Web部署
IDEA里面的facets和artifacts的讲解
上面的文章已经对项目配置信息解释得很清楚了,相信认真看完之后,都能明白如何配置。需要强调的是,别对目录结构有疑惑,web
资源原封不动输出,src
编译输出到WEB-INF
,种种设计是为了让我们能够更方便地开发web
项目。
Run/Debug Configurations
:理解了上面文章里的项目配置后,关于Run/Debug Configurations
的Tomcat
设置,也不在话下,需要注意的是,记得在Deployment
选项中给Tomcat
添加资源路径,也就是将url
与web应用的资源包进行映射,此处显示的应用资源包就是out
→ artifacts
→ web_war_exploded
目录。
Servlet
的调用过程服务器处理请求的过程:
Host
,分析访问的是哪台主机。web.xml
文件,是否有对应的虚拟路径,有则使用该路径对应的资源(如Servlet
)做响应。Servlet生命周期:
Servlet在第一次被访问到的时候,服务器会创建出Servlet对象,并立即调用init方法做初始化操作,创建出来的对象会一直驻留在内存中,之后对这个Servlet的访问都会导致其中的Service方法执行。当web应用移除容器或服务器关闭后,Servlet会启动destroy方法,进行销毁。
Servlet的继承结构:
Servlet接口定义了Servlet应该具有的基本方法,GenericServlet是通用的Servlet实现,对于不常用的方法在这个实现类中进行了基本实现,而Service设计为了抽象方法,需要子类去实现。HttpServlet是在通用Servlet的基础上基于HTTP协议进行了进一步的强化,实现了Service方法,并判断当前的请求方式,对应去调用doGet或doPost方法,所以一般我们开发Servlet只需要继承HttpServlet即可。
Servlet
细节问题一个Servlet可以对应多个mapping
,因此一个Servlet可以有多个路径来访问。
url-pattern
中的路径可以使用*
匹配符号进行配置,但是要注意,只能是/
开头/*
结尾,或者*.后缀
这两种方式。
如果一个url
匹配到多个Servlet,会调用更精确mapping
规则的那个Servlet
,*.后缀
的优先级最低。
如果在servlet
元素中配置了一个
元素,那么web应用在启动时就会装载并创建Servlet
的实例对象,以及调用init
方法,而不会等到第一次使用才创建。标签中的数字代表启动顺序。
缺省的Servlet配置:如果有一个Servlet的url-pattern
被配置成了/
,那么其他规则匹配不上的请求就会由这个Servlet来处理。其实对静态资源的访问,就是Tomcat
提供的缺省Servlet
来处理的,甚至404等提示页面也是由缺省Servlet
来处理的,所以一般我们不改动缺省配置。
由于Servlet
默认在内存中只有一个对象,当多个线程同时运行,可能会引发线程安全问题。即使用同步锁来解决,效率也比较低,因此我们尽量避免使用类(静态)变量。
ServletConfig
代表当前Servlet
在web.xml
中的配置信息。
在
Servlet
的配置文件中,可以使用一个或多个标签为
servlet
配置一些初始化参数。当Servlet
配置了初始化参数后,web容器在创建Servlet
实例对象时,会自动将这些初始化参数封装到ServletConfig
对象中,并在调用servlet
的init
方法时,将ServletConfig
对象传递给servlet
,进而开发者可以通过该对象获取当前servlet
的初始化参数信息。
<servlet>
<servlet-name>DemoNameservlet-name>
<servlet-class>cn.demo.FirstServletservlet-class>
<init-param>
<param-name>param1param-name>
<param-value>value1param-value>
init-param>
servlet>
可以获取当前Servlet
在web.xml
中配置的名称;
可以获取当前Servlet
中配置的初始化参数;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取Servlet的config对象
ServletConfig config = this.getServletConfig();
//获取当前Servlet在web.xml中配置的名称
String sName = config.getServletName();
//获取配置的初始化参数
String value = config.getInitParameter("param1");
}
ServletContext
web容器(Tomcat
)在启动时,会为 每个 web应用程序都创建一个ServletContext
对象,它代表当前web应用。
如何获取一个ServletContext
对象:
ServletConfig config = this.getServletConfig();
ServletContext context = config.getServletContext();
//或者直接使用this.getServletContext();其实一样的原理
//this指的是HttpServlet对象
ServletContext
对象有哪些作用?
ServletContext
作为域对象,可以在整个web应用范围内共享数据。
ServletContext context = this.getServletContext();
//一个Servlet设置属性: void setAttribute(String, Object)
context.setAttribute("name", "Tom");
//在另一个Servlet中使用域对象获取属性: Object getAttribute(String)
//注意获取的结果是一个对象,需要转换类型才能输出
String v = (String)context.getAttribute("name"); //Tom
//删除属性
context.removeAttribute("name");
生命周期:
当服务器启动web应用创建出ServletContext
对象后,域产生。
当web应用被移除或服务器关闭,随着web应用的销毁域也销毁。
前面我们提到ServletConfig
可以获取当前Servlet
的一些初始配置参数,那我们如何获取ServletContext
(web应用)的初始化配置信息呢?
配置共享参数:
<context-param>
<param-name>param1param-name>
<param-value>value1param-value>
context-param>
获取初始化配置信息:
//获取域对象
ServletContext context = this.getServletContext();
//获取应用的初始化配置信息
String value = context.getInitParameter("param1");
//可以使用枚举,获取完整的信息
Enumeration enumeration = context.getInitParameterNames();
while(enumeration.hasMoreElements()){
String name = (String) enumeration.nextElement();
String value = context.getInitParameter(name);
System.out.println(name+":"+value);
}
现在需要理解几个名词:
请求参数:
浏览器发送过来的请求中的参数信息。
初始化参数:
在web.xml中为
Servlet
或ServletContext
配置的初始化时带有的基本参数。
域属性:
四大作用域中存取的键值对。(后面会提到)
请求转发:服务器内部进行资源流转。
注意:
请求转发是一次请求一次响应,而请求重定向是两次请求两次响应。
DispatchServlet
:
//获取ServletContext对象,调用方法再获取请求转发对象
ServletContext sc = this.getServletContext();
//创建资源转发对象RequestDispatcher,参数为转向的路径
RequestDispatcher rd = sc.getRequestDispatcher("/news/goal");
//实现资源的转发,目的地:DestServlet
rd.forward(request, response);
DestServlet
:
//接收转发过来的请求
response.getWriter().write("I received the request from the dispatcher");
在Servlet
中读取资源文件时:
由于路径会相对于程序启动的目录,在web环境下,就是tomcat启动的目录,所以会找不到资源文件。为了解决这样的问题,
ServletContext
提供了getRealPath
方法,在这个方法中传入一个路径,这个方法的底层会在传入路径前拼接当前web应用的硬盘路径,从而得到当前资源的硬盘路径,这种方式可以在任何发布环境下获取正确的资源路径。
//可以利用ServletContext读取资源文件,如:根目录的config.properties
//先看看默认会在哪里找资源
File file = new File("config.properties");
System.out.println(file.getAbsolutePath());
// ...tomcat\bin\config.properties 会去tomcat的启动目录中寻找资源
//用Properties读取资源信息
Properties prop = new Properties();
//从指定的路径加载读取资源
prop.load(new FileReader(this.getServletContext().getRealPath("config.properties")));
System.out.println(prop.getProperty("username"));
System.out.println(prop.getProperty("password"));
在实际场景中,往往是Servlet
收发请求,而业务逻辑的处理则在另外的Service
类中执行,这种类没有继承HttpServlet
,没有ServletContext
,该如何获取资源文件呢?
如果在非Servlet
环境下要读取资源文件,可以采用类加载器加载文件的方式读取资源。
//HandleServlet.java
//这里主要收发请求
//调用service的方法去处理业务逻辑
Service ser = new Service();
ser.method1();
--------------------------------------
//Service.java
public class Service {
//在这里处理大部分的业务逻辑(Servlet只负责收发请求)
public void method1() throws IOException {
Properties prop = new Properties();
//使用类加载器去加载资源,默认当前目录是WEB-INF的classes下
prop.load(new FileReader(Service.class.getClassLoader().getResource("../../config.properties").getPath()));
//或者使用getResource("...").toString()获取路径
//读取文件
System.out.println("ClassLoader get username:"+prop.getProperty("username"));
System.out.println("ClassLoader get password:"+prop.getProperty("password"));
}
}
ServletResponse
ServletResponse
是通用的response
,提供了一个响应应该具有的基本属性和方法。
而HttpServletResponse
在ServletResponse
的基础上针对于Http协议强化了一些属性和方法。
方式一 getOutputStream
:
//将字符串编码转换为输出流发送
response.getOutputStream().write("中国".getBytes());
//如果getBytes()不指定编码,则按照操作系统的默认去编码,即GB2312
//而浏览器在网页没有指定charset时默认也是按照操作系统默认编码来解码
//一般网络传输使用utf-8字符集,所以应该指定编码
response.getOutputStream().write("中国".getBytes("utf-8"));
//同时应该在响应头明确指定浏览器的解码方式
response.setHeader("Content-Type", "text/html;charset=utf-8");
方式二 getWriter
:
//效果和setHeader("Content-Type", "text/html;charset=utf-8")一样
response.setContentType("text/html;charset=utf-8");
//指定在发送字符串时使用的编码格式,如果不指定,会使用iso8859-1
//response.setCharacterEncoding("utf-8");
//其实这句在指定Content-Type时会自动调用
//编解码格式一致,可以发送
response.getWriter().write("中国");
文件下载需要使用到一个响应头属性:Content-Disposition
,需要注意,在headers
中只能出现iso8859-1
中的字符,不能出现中文,因此文件名如果是是中文,需要经过url编码才能传输,使用类URLEncoder
编码,URLDecoder
解码。
//headers中不能出现中文
response.setHeader("Content-Disposition","attachment;filename="+URLEncoder.encode("美女.jpg","utf-8"));
//获取(文件)输入流
InputStream in = new FileInputStream(this.getServletContext().getRealPath("1.jpg"));
//创建输出流
OutputStream out = response.getOutputStream();
byte[] bs = new byte[1024];
int i = 0;
//写入到输出流
while((i=in.read(bs)) != -1){
out.write(bs, 0, i);
}
//关闭输入流
in.close();
编解码:
String str = "中国";
String str2 = URLEncoder.encode(str, "utf-8");
String str3 = URLDecoder.decode(str2, "utf-8");//中国
//每隔一秒刷新一次页面,输出时间
response.getWriter().write(new Date().toLocaleString());
response.setHeader("Refresh", "1");
//使用最多的:重定向
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("注册成功,三秒后回到主页...");
response.setHeader("refresh", "3;url=/index.jsp");
//更常用的,是在一个html页面中设置重定向
"Refresh" content="3;url=/index.jsp">
利用response
设置状态码为302
,并设置响应头Location
为要重定向到的地址,就可以实现请求重定向操作了。
response.setStatus(302);
response.setHeader("Location", "/index.jsp");
//或者直接简化成一条语句
response.sendRedirect("/index.jsp");
在大部分时候请求重定向和转发的效果是差不多的,这时推荐使用转发,以减少对服务器的访问。而在某些情况下是需要使用转发的,目的往往是为了改变浏览器地址栏里的地址(如登录成功后转到主页)和更改刷新操作(如加入商品到购物车后转到购物车页面的操作)。
控制不做缓存:
response.setIntHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
控制缓存时间:
//缓存一个月。setDateHeader代表设置属性值为日期类型
//是long类型的数值,为时间戳,在这个时间戳之前缓存有效
response.setDateHeader("Expires", System.currentTimeMillis()+1000l*3600*24*30);
//别忘了加l,代表long型数值,只要运算中有任意一个数带l,结果就是long型
//如果不加l,由于int类型容不下这么大的数值,往上累计,最终会变成负数
getOutputStream
和getWriter
这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一个方法。转发是一次请求,一次响应。因此在Servlet
转发的时候,注意在两个Servlet
中使用相同的方法。
Servlet
程序向ServletOutputStream
或PrintWriter
对象中写入的数据将被Servlet
引擎从response
里面获取,这些数据会作为响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
Servlet
的service
方法结束后,Servlet
引擎将检查getWriter
或getOutputStream
方法返回的输出流对象是否已经调用过close
方法,如果没有,tomcat
将自动调用close
方法关闭该输出流对象。所以一般不要自己在Servlet
中关闭这个流,但输入流需要自己关闭。
方法介绍:
建立BufferedImage对象,指定图片的长宽和类型
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
取得Graphics对象,用来绘制图片
Graphics graphics = image.getGraphics();
绘制背景颜色
graphics.setColor(Color.WHITE);
graphics.fillRect(0,0,width,height);
绘制边界
graphics.setColor(Color.BLUE);
graphics.drawRect(0,0,width-1,height-1);
生成随机数
Random random = new Random();
random.nextInt(n);//生成0到n的随机数,包括0,不包括n
绘制干扰线
graphics.drawLine(x1,y1,x2,y2);//线段的两个端点,随机生成
设置字体
如果验证码是中文,要使用中文的字体库
graphics.setFont(new Font("宋体", Font.PLAIN, 20));//字体,粗细倾斜等,size
通过词库生成随机验证码内容
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"
如果是汉字:\u4e00 —— \u9fa5
graphics.drawString(str, x, y);//str是字符串,x是left,y是top(偏移量)
设置旋转
Graphics2D graphics = (Graphics2D)image.getGraphics();
graphics.rotate(theta, x, y);
//注意这里的theta是弧度,如果旋转45度,要写成 45d/180*Math.PI或0.25*Math.PI
//否则45/180会是0,45d代表double类型的45
释放此图形的上下文以及它使用的所有系统资源
graphics.dispose();
通过ImageIO
对象的write
静态方法将图片输出(输出后response已经commit了)
ImageIO.write(image, "jpg", res.getOutputStream());
验证码的绘制:
//1.在内存中构建一张图片
int height = 30;
int width = 120;
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//2.获取图像上的画布
Graphics2D g = (Graphics2D)img.getGraphics();
//3.设置背景色
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0,0,width,height);
//4.设置边框
g.setColor(Color.BLUE);
g.drawRect(0,0,width-1,height-1);
//5.画干扰线,线的两端坐标都是随机生成的
for (int i = 0; i < 5; i++) {
g.setColor(Color.RED);
g.drawLine(randNum(0, width), randNum(0, height), randNum(0, width), randNum(0, height));
}
//6.绘制汉字
String base = "\u7684\u4e00\u4e86\u662f";//这里可以放置大量常用汉字的Unicode编码
for (int i = 0; i < 4; i++) {
//适当把rgb值往前调整,可避免字的颜色过分亮丽,影响人的识别
g.setColor(new Color(randNum(0,150),randNum(0,150),randNum(0,150)));
g.setFont(new Font("黑体", Font.BOLD, 20));
//让画布适当旋转
int r = randNum(-45, 45);
g.rotate(1.0*r/180*Math.PI, 5+(i*30), 22);
g.drawString(base.charAt(randNum(0,base.length()-1))+"",5+(i*30), 22);
//绘制完一个字,旋转回原位,否则后面的字会在该基础上继续旋转
g.rotate(-1.0*r/180*Math.PI, 5+(i*30), 22);
}
//7.将图片输出到浏览器
ImageIO.write(img, "jpg", response.getOutputStream());
//注意,此时,response已经结束,如果在下面再写setHeader、write是无效的
//细节问题
System.out.println(45/180*Math.PI);//0.0
System.out.println(45d/180*Math.PI);
System.out.println(0.25*Math.PI);
//一个用来生成随机数的方法
private Random rand = new Random();
private int randNum(int begin, int end){
return rand.nextInt(end-begin) + begin;
}
同时要让验证码页面不缓存,每次刷新:
response.setDateHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
注意html页面在使用验证码的时候,切换验证码只需改变src即可再次发送请求,后台不需改动:
function changeImg(img){
img.src = "/news/valiImg?time=" + new Date().getTime();
}
ServletRequest
同理,ServletRequest
也是一个通用的request
,提供一个request
应该具有的基本方法,而HttpServletRequest
是针对http
协议进行了进一步的增强的request
。
//获取客户端请求的完整url
String url = request.getRequestURL().toString();
//获取客户端请求的资源部分名称
String uri = request.getRequestURI();// /news/index.jsp
//获取请求行中参数部分
String qStr = request.getQueryString();//name=Tom&age=18
//获取请求客户端的ip地址
String ip = request.getRemoteAddr();
//获取客户机的请求方式
String method = request.getMethod();
//获取当前web应用的名称,可以用这个来设置路径,以便后续项目迁移
String name = request.getContextPath();
response.sendRedirect(request.getContextPath()+"/index.jsp")
String value = request.getHeader("Host");
//获取所有请求头
Enumeration enumeration = request.getHeaderNames();
while(enumeration.hasMoreElements()){
String name = enumeration.nextElement();
String value = request.getHeader(name);
System.out.println(name+":"+value);
}
//获取具体类型的客户机请求头
getIntHeader(name)方法 —— int
getDateHeader(name)方法 —— long(日期对应毫秒)
使用referer请求头防盗链:
//假设别人在自己网站上使用一个a标签盗用了我们的localhost/news.html,我们可以在请求头中验证referer是否为我们可信的站点,如果不是,就不让访问,并且重定向到我们的主页
String ref = request.getHeader("Referer");
if(ref==null || "".equals(ref) || !ref.startsWith("http://localhost")){
response.sendRedirect(request.getContextPath()+"/index.html");
}
getParameter(name) —— String 通过name获得值
getParameterValues —— String[] 通过name获得多值 checkbox
getParameterNames —— Enumeration<String> 获得所有name
getParameterMap —— Map<String,String[]> key:name,value:多值
解决参数传递过来乱码的问题:
一般在html表单中数据是utf-8
编码,而服务器中默认使用的编码是iso8859-1
,对于表单中的中文数据是无法解码的,因此需要显式指定请求的解码方式:
request.setCharacterEncoding("utf-8");
注意,上面指定的是服务器以什么编码来解码http请求的实体内容,所以只适合POST请求,而GET请求的参数是附带在URL后的,需要我们手动进行编解码:
//对于get提交参数中的乱码,需要我们先按iso8859-1编码为字节,然后再按utf-8解码为字符串。
String username = request.getParameter("username");
username = new String(username.getBytes("iso8859-1"), "utf-8");
//当然也可以解码post请求
作用范围:整个请求链上。
生命周期:当服务器收到一个请求,创建出代表请求的request对象,request域开始,当请求结束,服务器销毁代表请求的request对象,request域结束。
方法:
setAttribute
getAttribute
removeAttribute
//其实四大作用域都有这三个方法。
使用请求域:
String value = (String) request.getAttribute("name"); //返回一个对象,需要类型转换
String result = "apple";
request.setAttribute("result", result);
//对于请求附带的参数,使用request域即可,ServletContext域范围过大
//this.getServletContext().getRequestDispatcher("/servlet/demo").forward(request,response);
request.getRequestDispatcher("/show.jsp").forward(request,response);
//在show.jsp中使用request域的属性
<%= (String) request.getAttribute("result") %>
作用:
在整个请求链范围内共享数据,通常我们在Servlet
中处理好的数据会存入request
域后将请求转发到jsp
页面来进行展示。
可以像ServletContext
对象一样实现请求转发:
request.getRequestDispatcher("").forward(request,response);
请求转发时,如果已经有数据被写入到了request的缓冲区,但这些数据还没有被发送到客户端,则请求转发时,这些数据将会被清空。
response.getWriter().write("something...");
resquest.getRequestDispatcher("/servlet/demo").forward(request,response);
//servlet.demo
response.getWriter().write("other things...");
//最后输出的只是other things,而something被清空了
但是被清空的只是响应中的实体内容,头信息并不会被清空(比如setContentType("text/html;charset=utf-8")
)。
如果写入了缓冲区,并且还强制刷新了缓冲区,则会报错:
response.getWriter().write("something...");
response.getWriter().flush();//将缓冲区信息输出
resquest.getRequestDispatcher("/servlet/demo").forward(request,response);//会报错,无法转发
请求转发时,如果已经有数据发送给了浏览器,那么再进行请求转发,不能成功,会抛出异常。
一个Servlet
中两次请求转发也是不行的。
请求包含:
将两个资源的输出进行合并后再输出,同样也是使用了转发器对象。
this.getServletContext().getRequestDispatcher("").include(request,response);
request.getRequestDispatcher("").include(request,response);
被包含的Servlet
程序不能改变响应消息的状态码和响应头,如果它里面存在这样的语句,这些语句的执行结果将被忽略。
//servletA.java
response.getWriter().write("from servletA");
request.getRequestDispatcher("/servletB").include(request,response);
//servletB.java
response.getWriter().write("from servletB");
//最后会输出 from servletA from servletB
//这就是请求包含的作用,一般常用来做页面布局,将重复的header footer包含
请求重定向:response.sendRedirect();
请求转发: request.getRequestDispatcher().forward();
请求包含: request.getRequestDispatcher().include();
request
域传递域属性则必须使用请求转发。