Servlet简介
1.Servlet是sun公司提供的一门用于开发动态web资源的技术
*静态web资源:固定数据文件
*动态web资源:通过程序动态生成数据文件
2.Servlet技术基于Request-Response编程模型 ---- HTTP协议也是基于请求响应模型
*Servlet技术用来开发基于HTTP web应用程序
3.Servlet快速入门
1)创建web project
2)编写class继承HttpServlet
3)在web.xml配置Servlet程序,虚拟访问路径
*用户在浏览器上通过这个路径访问编写Servlet程序
4)覆盖doGet或者doPost方法进行输出
eg:
package cn.lsl.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("get请求");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("post请求");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
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_2_5.xsd">
<!-- 为HelloServlet配置浏览器可以访问虚拟路径 -->
<servlet>
<!-- 为Servlet程序命名 -->
<servlet-name>HelloServlet</servlet-name>
<!-- Servlet全路径:包名.类名 -->
<servlet-class>cn.lsl.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<!-- 为Servlet程序制定浏览器访问虚拟路径 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 用户在浏览器通过/hello访问Servlet -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
4.Servlet动态生成网页文件
eg:
package cn.lsl.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("get请求");
//设置响应流编码问题
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();//获得向浏览器输出流
out.println("<html>");
out.println("<head>");
out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
out.println("</head>");
out.println("<body>");
out.println("<h1>这是一个由Servlet动态生成页面!</h1>");
out.println("</body>");
out.println("</html>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("post请求");
}
}
Servlet生命周期
1.Servlet接口定义了Servlet生命周期
init()方法:服务器调用该方法初始化Servlet
service()方法:初始化完毕,服务器调用该方法响应客户的请求
destory()方法:服务器调用该方法消灭servlet对象
1)tomcat服务器启动时,没有创建Servlet对象
2)第一次访问时,tomcat构造Servlet对象,调用init,执行service
3)从第二次以后访问tomcat不会从新创建Servlet对象,也不会调用init ---- 每一次访问都会调用service
4)当服务器重启或正常关闭,调用destroy(正常关闭 shutdown.bat)
eg:
package cn.lsl.servlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class InitServlet implements Servlet{
public InitServlet(){
System.out.println("构造了InitServlet对象...");
}
@Override
public void init(ServletConfig arg0) throws ServletException {
// TODO Auto-generated method stub
System.out.println("初始化....");
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("服务....");
}
@Override
public void destroy() {
// TODO Auto-generated method stub
System.out.println("销毁....");
}
@Override
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getServletInfo() {
// TODO Auto-generated method stub
return null;
}
}
//第一次访问输出的结果:
//构造了InitServlet对象...
//初始化....
//服务....
//第二次访问输出的结果:
//服务....
其中,init()方法只在Servlet第一次被请求加载的时候被调用一次,当有客户再请求Servlet服务时,Web服务器将启动一个新的线程,在该线程中,调用service方法响应客户的请求。
2.Servlet对象是tomcat创建的,每次请求调用Servlet中service方法,tomcat服务器会在每次调用Servlet的service方法时,为该方法创建Request对象和Response对象。
*JavaEE API中没有Request和Response实现类 ----- 实现类由Servlet服务器提供的,tomcat提供实现类(weblogic提供实现类)。
Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
3.针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其他请求服务,直至web容器退出,Servlet实例对象才会销毁。
4.service方法和HttpServlet doGet/doPost关系区别?
在HttpServlet代码实现中,根据请求方式不同 调用相应doXXX方法,get方式请求--- doGet post方式 --- doPost
5.配置Servlet随tomcat服务器启动时,进行初始化 --- <load-on-startup>
*<load-on-startup>参数可以是一个数字 0-9 代表服务器加载优先级,0最高
eg:
<servlet>
<servlet-name>InitServlet</servlet-name>
<servlet-class>cn.itcast.servlet.InitServlet</servlet-class>
<!-- 配置 Servlet在服务器启动时 进行加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
总结:
在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。
对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,
service方法再根据请求方式分别调用doXXX方法。
路径匹配
一个Servlet可以配置多个url-pattern
URL配置格式三种:
1.完全路径匹配(以/开始)例如:/hello /init
查错:
* 当前工程没有被正确发布,访问该工程所有静态资源、动态资源 发生404 ----- 工程启动时出错了
* 查看错误时 分析错误
1) 单一错误 : 从上到下 查看第一行你自己写代码 (有的错误与代码无关,查看错误信息)
2)复合错误 Caused by ---- 查看最后一个Caused by
* Invalid <url-pattern> init2 in servlet mapping
2.目录匹配(以/开始) 例如:/* /abc/*
/代表网站根目录
3.扩展名匹配(不能以/开始) 例如: *.do *.action
典型错误 /*.do
优先级:完全匹配>目录匹配>扩展名匹配
路径问题
编写九九乘法表
1、需要用户在客户端输入一个数字
2、Servlet接受客户端输入数字,打印对应乘法表
eg:
package cn.lsl.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ChengfabiaoServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获得请求提交数据number
String numberStr = request.getParameter("number");
int number = 0;
try {
number = Integer.parseInt(numberStr);
} catch (NumberFormatException e) {
throw new RuntimeException("输入的不是整数");
}
//打印九九表
PrintWriter out = response.getWriter();
for(int i=1; i<=number; i++){
for(int j=1; j<=i; j++){
out.print(j + "*" + i + "=" + j * i + " ");
}
out.print("<br>");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
在根目录下的chengfabiao.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>乘法表</title>
</head>
<body>
<!-- 相对路径 chengfabiao -->
<form action="/ServletTest/chengfabiao" method="post">
请输入一个数字<input type="text" name="number">
<input type="submit" value="打印乘法表"/>
</form>
</body>
</html>
在aaa文件夹下的chengfabiao.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>乘法表</title>
</head>
<body>
<!-- 相对路径 ../chengfabiao -->
<form action="/ServletTest/chengfabiao" method="post">
请输入一个数字<input type="text" name="number">
<input type="submit" value="打印乘法表"/>
</form>
</body>
</html>
分析:
在chengfabiao.html 通过 action 访问 ChengfabiaoServlet 路径可以用绝对路径和相对路径
相对路径:相对当前网页地址 路径 例如 chengfabiao ./chengfabiao ../chengfabiao
例如: http://localhost/ServletTest/chengfabiao.html 提交 action="chengfabiao"
* 将url最后地址换成相对路径
结果: http://localhost/ServletTest/chengfabiao ----- 服务器端 /chengfabiao
例如: http://localhost/ServletTest/aaa/chengfabiao.html 提交 action="chengfabiao"
结果: http://localhost/ServletTest/aaa/chengfabiao ----- 服务器 /chengfabiao
* /aaa/chengfabiao 与服务器 /chengfabiao 不匹配 出现404
http://localhost/ServletTest/aaa/chengfabiao.html 提供 action="../chengfabiao"
结果:http://localhost/ServletTest/aaa/../chengfabiao ---- > ..和/aaa抵消 http://localhost/ServletTest/chengfabiao 可以匹配服务器 /chengfabiao
结论:如果用相对路径提交请求,考虑当前路径, 当前访问服务器资源路径不同 ---- 相对路径写法不同
绝对路径 解决相对路径,会根据当前地址改变问题。 例如: /ServletTest/chengfabiao 、http://localhost/ServletTest/chengfabiao
绝对路径 以/开始 /访问服务器根目录
例如: 客户端访问服务器,不管当前路径是什么 --- / 服务器根目录 http://localhost
/ServletTest --- 找到虚拟目录ServletTest工程 /ServletTest/chengfabiao --- 找到 ServletTest工程下配置 虚拟路径/chengfabiao
结论: 客户端路径 /工程虚拟目录/servlet虚拟路径 例如:/ServletTest/chengfabiao
服务器端 配置web.xml 不需要写工程虚拟目录 只要直接写/servlet虚拟路径 例如:/chengfabiao
ServletConfig
1.init方法 --- init(ServletConfig) --- 通过ServletConfig获得Servlet初始化参数
2.在web.xml中<servlet>标签内通过<init-param>标签为Servlet配置初始化参数
<init-param>
<param-name>address</param-name>
<param-value>软件园</param-value>
</init-param>
3.在Servlet程序中通过ServletConfig对象获得address对应数据
getInitParameter ----- 通过name获得value
getInitParameterNames ---- 获得所有name
*思考:如何在doGet 或 doPost 方法中 获得 Servlet初始化参数
GenericServlet 已经将ServletConfig 保存成员变量 ----- 在子类中通过 getServletConfig方法 获得 初始化参数
结论:子类Servlet不需要覆盖init(ServletConfig),只需要通过GenericServlet中getServletConfig()获得ServletConfig对象
应用:在init-param 指定配置文件位置和名称,配置Servlet随服务器启动创建 load-on-startup
*ServletConfig配置初始化数据,只能在配置Servlet获得,其他Servlet无法获得 --- 每个Servlet程序都对应一个ServletConfig对象
ServletContext
ServletContext是Servlet上下文对象
每一个工程对应创建单独ServletContext对象,这个对象代表当前web工程
操作ServletContext必须通过ServletConfig获得对象
应用:
1.获得整个web应用初始化参数
<!-- 配置全局初始化参数,所有Servlet都可以访问 -->
<context-param>
<param-name>hobby</param-name>
<param-value>唱歌</param-value>
</context-param>
package cn.lsl.servlet;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ContextServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取hobby全局参数
//通过ServletConfig获得ServletContext
//ServletContext context = getServletConfig().getServletContext();
//上面写法可以简化一下
ServletContext context = getServletContext();
//读取全局初始化参数
System.out.println(context.getInitParameter("hobby"));
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
*ServletConfig对象配置参数,只对配置Servlet有效(ServletConfig每个Servlet对应一个)
ServletContext对象配置参数对所有Servlet都可以访问(ServletContext每个工程(web应该)对应一个)
config参数<init-param>
context参数<context-param>
2.实现全局数据共享
package cn.lsl.servlet;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CountServlet extends HttpServlet {
@Override
public void init() throws ServletException {
//想ServletContext保存访问次数
ServletContext context = getServletContext();
//保存数据setAttribute
context.setAttribute("visittimes", 0);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//从ServletContext中获得访问次数
ServletContext context = getServletContext();
int times = (Integer)context.getAttribute("visittimes");
//访问次数+1
times++;
//将访问次数更新回去 ServletContext
context.setAttribute("visittimes", times);
System.out.println("网站被访问了一次");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
package cn.lsl.servlet;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CountShowServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletContext context = getServletContext();
int times = (Integer)context.getAttribute("visittimes");
response.getWriter().println("web site has been visitted:" + times + " times!");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
在ServletContext中,保存站点访问次数,每一个用户访问站点,将访问次数+1
在ServletContext初始化过程中,向ServletContext保存访问次数0,-------ServletContext setAttribute
每次访问次数+1 --- 数据存放ServletContext中,所有Servlet都可以获得该数据
*在ServletContext中保存数据,所有Servlet都可以访问
3.实现服务器端转发功能
什么是转发?转发和重定向区别?
转发:如果服务器端处理客户端请求时,如果需要多个服务器程序同时进行处理,需要采用转发操作,转发操作对客户端一次请求,在服务器端通过多个程序连续执行,进行处理
重定向:服务器进行处理后,需要通知客户端访问下一个目标程序继续处理
区别:
1)转发产生一次请求一次响应,重定向产生两次请求两次响应
2)转发对客户端不可见的(未知),重定向客户端可以察觉到(可见)
3)转发是url显示转发钱资源路径,重定向url显示定向后资源路径
使用转发还是重定向? --- 转发性能好于重定向,请求次数少
转发的例子
eg:
package cn.lsl.servlet;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LetterCountServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String content = request.getParameter("content");
content = content.toUpperCase(); //将内容转为大写
//分析统计 --- 忽略大小写
int times[] = new int[26];
//遍历每一个字母
for(int i=0; i<content.length(); i++){
char c = content.charAt(i);
//判断字母是不是26个字母之一
if(Character.isLetter(c)){
times[c - 'A']++;
}
}
//交个下一个Servlet显示,将统计结果保存ServletContext
ServletContext context = getServletContext();
context.setAttribute("times", times);
//转发跳转另一个Servlet
RequestDispatcher dispatcher = context.getRequestDispatcher("/servlet/result");
dispatcher.forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
package cn.lsl.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LetterResultServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//从ServletContext获得数据
ServletContext context = getServletContext();
int[] times = (int[])context.getAttribute("times");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
for(int i=0; i<times.length; i++){
char c = (char)('A' + i);
//次数
int ltimes = times[i];
out.println("字母:" + c + "出现了" + ltimes + "次!<br/>");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
eg:
package cn.lsl.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("AServlet 执行...");
// 转发
ServletContext context = getServletContext();
// 转发给BServlet 这里 / 当前web 工程 /day05
RequestDispatcher dispatcher = context.getRequestDispatcher("/B");
dispatcher.forward(request, response);
// // 重定向 / 代表当前web服务器
// response.setStatus(302);
// response.setHeader("Location", "/ServletTest/B"); // 从客户端访问,必须含有工程路径
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
package cn.lsl.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class BServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("BServlet 执行 ...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
4.读取web工程资源文件
package cn.lsl.servlet;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ReadFileServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//读取a1.txt出现WEB-INF/classes
String filename2 = "/WEB-INF/classes/a1.txt";
filename2 = getServletContext().getRealPath(filename2);
readfile(filename2);
//读取a2.txt位于网站根目录
String filename = "/a2.txt";
//通过/开始路径获得绝对磁盘路径
filename = getServletContext().getRealPath(filename);
System.out.println(filename);
readfile(filename);
//因为a1.txt位于/WEB-INF/classes --- 类路径中 --- 通过Class对象读取文件
Class c = ReadFileServlet.class;
//返回磁盘绝对路径
String filename3 = c.getResource("/a1.txt").getFile();
System.out.println(filename3);
readfile(filename3);
}
public static void readfile(String filename) throws IOException{
BufferedReader in = new BufferedReader(new FileReader(filename));
String line;
while((line = in.readLine()) != null){
System.out.println(line);
}
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
使用Servlet读取文件,只能读取WebRoot下所有文件 --- 必须使用绝对磁盘路径
通过站点根目录绝对路径 获得磁盘绝对路径 ------ getServletContext().getRealPath(“/WEB-INF/info.txt”)
* 因为 WEB-INF/classes 非常特殊 (存放.class文件目录),被类加载器加载,通过Class类对象读取 该目录下文件
String filename3 = c.getResource("/a1.txt").getFile(); ----- / 代表 /WEB-INF/classes
结论:在web工程中,必须将 文件路径转换绝对磁盘路径 c:\xxx e:\xxx\xxx ----- getServletContext().getRealPath("/xxx"); /代表WebRoot
如果读取文件 恰好位于 WEB-INF/classes ----- 通过 类名.class.getResource("/文件名").getFile(); 获得绝对磁盘路径 / 代表 /WEB-INF/classes
缺省Servlet
缺省Servlet功能:处理其他Servlet都不处理请求
tomcat/conf/web.xml org.apache.catalina.servlets.DefaultServlet作为缺省Servlet
package cn.lsl.servlet;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DefaultServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("缺省Servlet执行了");
//将用户访问
//1.获得用户访问目标资源路径
String path = request.getRequestURI().substring(request.getContextPath().length());
System.out.println(path);
//2.判断文件是否存在 --- 读取磁盘绝对路径
String filename = getServletContext().getRealPath(path);
File file = new File(filename);
if(file.exists()){
InputStream in = new FileInputStream(file);
OutputStream out = response.getOutputStream();
int b;
while((b = in.read()) != -1){
out.write(b);
}
in.close();
out.close();
}else{
response.setStatus(404);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}