JavaEE实战——Servlet入门Servlet生命周期绝对路径ServletContext
Posted 李春春_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaEE实战——Servlet入门Servlet生命周期绝对路径ServletContext相关的知识,希望对你有一定的参考价值。
前言
接下来的三篇博客我会分别介绍Servlet的以下三个方面:
1、Servlet程序编写 ----- 生命周期2、ServletAPI Request Response
3、Cookie 和 Session
Servlet的作用:Servlet 用来 动态web资源 开发
静态web资源 : 固定数据文件动态web资源 : 通过程序动态生成数据文件
Servlet技术基于Request-Response编程模型 ---- HTTP协议也是基于请求响应 模型
* Servlet技术 用来 开发基于HTTP协议的 web 应用程序接触 JavaEE API ------ 程序 接口 和 已经实现接口 类的 使用
JavaEE ---- Java Platform, Enterprise Edition 缩写Servlet快速入门
创建步骤:
1、创建web project
2、编写 class 继承 HttpServlet3、在web.xml 配置 Servlet程序 虚拟访问路径
* 用户在浏览器上通过这个路径 访问编写Servlet程序
4、覆盖doGet或者doPost方法 进行输出
package ustc.lichunchun.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
// 覆盖 doGet 和 doPost
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
// 处理 get方式请求
System.out.println("get 请求 ...");
// 设置响应流 编码问题 (必须和html/meta标签中的charset一致)
resp.setContentType("text/html;charset=utf-8");
// 生成 hello.html 相同内容
PrintWriter out = resp.getWriter(); // 获得向浏览器输出流
// 通过 out 流 生成 html
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>Hello,这是一个 由Servlet 动态生成网页!</h1>");
out.println("</body>");
out.println("</html>");
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
// 处理 post方式请求
System.out.println("post 请求 ...");
配置细节:
|--<servlet></servlet>|--<servlet-name> 为Servlet程序 命名
|--<servlet-class> Servlet全路径:包名.类名
|--<servlet-mapping></servlet-mapping>
|--<servlet-name> Servlet 名称
|--<url-pattern> 用户在浏览器通过/hello 访问Servlet
<?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">
<!-- 配置全局初始化参数,所有Servlet都可以 访问 -->
<context-param>
<param-name>hobby</param-name>
<param-value>电音</param-value>
</context-param>
<!-- 用户 通过 /init 访问Servlet -->
<servlet>
<servlet-name>InitServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.InitServlet</servlet-class>
<!-- 配置 Servlet在服务器启动时 进行加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>InitServlet</servlet-name>
<url-pattern>/init</url-pattern>
<url-pattern>/init2</url-pattern>
</servlet-mapping>
<!-- 为 HelloServlet 配置 浏览器可以访问虚拟 路径 -->
<servlet>
<!-- 为 Servlet程序 命名 -->
<servlet-name>HelloServlet</servlet-name>
<!-- Servlet全路径 : 包名.类名 -->
<servlet-class>ustc.lichunchun.servlet.HelloServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>HelloServlet2</servlet-name>
<servlet-class>ustc.lichunchun.servlet.HelloServlet2</servlet-class>
</servlet>
<servlet>
<servlet-name>ChengfabiaoServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.ChengfabiaoServlet</servlet-class>
</servlet>
<servlet>
<!-- 在Servlet标签内部 配置初始化参数 -->
<servlet-name>ConfigServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.ConfigServlet</servlet-class>
<!-- 通过 init-param -->
<init-param>
<param-name>MyName</param-name>
<param-value>李春春</param-value>
</init-param>
<init-param>
<param-name>MySchool</param-name>
<param-value>中国科学技术大学</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>ContextServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.ContextServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>CountServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.CountServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>CountShowServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.CountShowServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>AServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.AServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>BServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.BServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>LetterCountServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.LetterCountServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>LetterResultServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.LetterResultServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>ReadFileServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.ReadFileServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>DefaultServlet</servlet-name>
<servlet-class>ustc.lichunchun.servlet.DefaultServlet</servlet-class>
</servlet>
<servlet-mapping>
<!-- 为Servlet程序 指定 浏览器访问 虚拟路径 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 用户 在 浏览器通过/hello 访问Servlet -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>HelloServlet2</servlet-name>
<url-pattern>/hello2</url-pattern>
<url-pattern>/abc/*</url-pattern>
<!-- 扩展名 匹配 必须不能 用 / 开始 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ChengfabiaoServlet</servlet-name>
<!-- 客户端绝对路径 /day05/chengfabiao -->
<url-pattern>/chengfabiao</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ConfigServlet</servlet-name>
<url-pattern>/config</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ContextServlet</servlet-name>
<url-pattern>/context</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>CountServlet</servlet-name>
<url-pattern>/count</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>CountShowServlet</servlet-name>
<url-pattern>/countshow</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AServlet</servlet-name>
<url-pattern>/a</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>BServlet</servlet-name>
<url-pattern>/b</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>LetterCountServlet</servlet-name>
<url-pattern>/servlet/lettercount</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>LetterResultServlet</servlet-name>
<url-pattern>/servlet/result</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ReadFileServlet</servlet-name>
<url-pattern>/readfile</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>DefaultServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Servlet作用:动态生成 网页文件
Servlet执行过程:
1、用户在客户端发起url请求 : http://localhost/day05/hello ----- web.xml /hello 映射 HelloServlet程序2、用户提交请求时,get方式提交 执行 HelloServlet的 doGet方法 post方式提交 执行 HelloServlet的doPost 方法
对Servlet编译运行类库的理解:
Servlet程序在编写和运行时,需要依赖javaee 类库 (API支持)(Jar包)
* 在学习javase List 需要 import java.util.List 需要 jre/lib/rt.jar* MyEclipse 自动导入 javaee5 liberary 存在 javaee.jar 提供 Servlet 需要类 API支持 (开发环境使Servlet程序正常编译)
* Serlvet程序运行tomcat环境中 没有javaee.jar , 在 tomcat/lib/servlet-api.jar 提供Servlet程序运行需要 类API 支持 (运行环境需要的)
示例:手动编写Servlet运行
1、在webapps 新建 day05test目录 --- 虚拟应用2、在day05test 新建 WEB-INF/classes
3、将编写Servlet的java源码文件 放入 classes ,在 WEB-INF 配置web.xml
4、编译Servlet的 java程序
javac -classpath E:\\apache-tomcat-6.0.14\\lib\\servlet-api.jar HelloServlet.java // 通过 -classpath 指定 Servlet需要jar 包
生成Servlet package结构
javac -d . -classpath E:\\apache-tomcat-6.0.14\\lib\\servlet-api.jar HelloServlet.java
这时,就可以在浏览器中使用 http://192.168.1.104/day05test/hello 来访问这个Servlet程序
Servlet运行原理剖析
编写Servlet程序没有 main函数 ---- tomcat调用Servlet程序执行
Servlet是一个供其他Java程序(Servlet引擎/Tomcat主程序)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
MyEclipse中Servlet模板修改
一般开发中,我们是不会手动编写Servlet程序的。
而是通过myeclipse向导 创建Servlet ---- 向导会帮你创建Servlet程序,同时自动生成web.xml 配置
* 生成Servlet信息非常复杂,想生成Servlet 内容整洁一些,精简一些 ------ 修改Servlet模板
1、myeclipse工具 ---- 安装目录 common / pluginscom.genuitec.eclipse.wizards_8.5.0.me201003052220.jar
2、解压缩 templates/Servlet.java --- 这个就是Servlet模板
3、com.genuitec.eclipse.wizards_9.0.0.me201108091322.jar 复制此文件jar包名字
进入你安装的MyEclipse的安装目录下的Common文件夹里 然后再搜索文件的地方粘贴上上面复制的文件名。
搜索出来后,右键 打开文件夹的位置,剪接出来。如果你想备份就剪接,不备份直接把下载的jar包丢进MyEclipse\\Common\\plugins文件夹里面即可,选择覆盖就OK了,重新在建立一个Servlet 试试效果吧!
在MyEclipse10中设置的,别的版本没测试过!
理解Servlet继承关系
通过API得到 Servlet 的继承关系
Servlet接口 ---- 实现类 GenericServlet ------ 子类 HttpServlet ------ 编写Servlet 继承HttpServlet
* 我们自己自定义编写的 Servlet 就间接实现了 Servlet 接口 (简化开发)
1、Servlet接口 提出,为了解决基于请求-响应模型数据处理 (并没有涉及与HTTP协议相关 API)
2、GenericServlet 实现接口 通用Servlet 也没有提供与 HTTP协议相关 API
3、HttpServlet 才引入与 HTTP协议相关 API所以说,我们现在基于HTTP协议进行web开发,只需要继承HttpServlet即可
Servlet生命周期
init(ServletConfig config) 初始化
service(ServletRequest req, ServletResponse res) 提供服务方法,响应客户请求
destroy() 销毁
1、tomcat服务器启动时,没有创建Servlet对象
2、第一次访问时,tomcat构造Servlet对象,调用 init,执行service3、从第二次以后的访问 tomcat 不会重新创建Servlet对象,也不会调用init ---- 每一次访问都会调用service
(第二次开始后的每次Servlet服务请求,web服务器将启动一个新线程调用service方法响应客户请求,但是Servlet对象是唯一的,不会再创建。详见InitServlet.java成员变量 i)
4、当服务器重启或正常关闭时 调用destroy (正常关闭 shutdown.bat)
package ustc.lichunchun.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;
//实现Servlet 接口 --- 感受生命周期
public class InitServlet implements Servlet
int i;
public InitServlet()
System.out.println("构造了 InitServlet对象 ...");
@Override
public void destroy()
System.out.println("销毁...");
@Override
public void init(ServletConfig arg0) throws ServletException
System.out.println("初始化 ...");
@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException
System.out.println("服务...");
System.out.println("i:" + i + " " + Thread.currentThread().getName());
i++;
@Override
public ServletConfig getServletConfig()
return null;
@Override
public String getServletInfo()
return null;
Servlet对象是tomcat创建的,每次请求都会调用Servlet中service方法,tomcat服务器会在每次调用Servlet的service方法时,为该方法创建Request对象和Response对象。
即:对于一个Servlet在每次请求访问时,Servlet引擎都会创建一个描述请求消息和一个描述响应消息的对象。其中描述请求消息的对象实现了HttpServletRequest接口,而描述响应消息的对象实现了HttpServletResponse接口。
* 但是,在 JavaEE API 中并没有Request和Response实现类 ----- 实现类由Servlet服务器提供的,tomcat提供实现类 weblogic提供实现类
service方法和HttpServlet doGet/doPost 关系区别
必须阅读HttpServlet源代码 --> HttpServlet.java中两个service()方法
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
HttpServletRequest request;
HttpServletResponse response;
try
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
catch (ClassCastException e)
throw new ServletException("non-HTTP request or response");
service(request, response);
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
String method = req.getMethod();
if (method.equals(METHOD_GET))
long lastModified = getLastModified(req);
if (lastModified == -1)
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
else
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000))
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
else
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
else if (method.equals(METHOD_HEAD))
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
else if (method.equals(METHOD_POST))
doPost(req, resp);
else if (method.equals(METHOD_PUT))
doPut(req, resp);
else if (method.equals(METHOD_DELETE))
doDelete(req, resp);
else if (method.equals(METHOD_OPTIONS))
doOptions(req,resp);
else if (method.equals(METHOD_TRACE))
doTrace(req,resp);
else
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
在HttpServlet代码实现中,service方法 会根据请求方式不同 调用相应doXXX方法 get方式请求 --- doGet、post方式 --- doPost
总结:
1、针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其他请求服务,直至web容器退出,Servlet实例对象才会销毁。
2、在Servlet的整个生命周期内,Servlet的init方法只被调用一次,而且默认是在第一次访问该Servlet程序时才会调用。
而对一个Servlet的每次访问请求都导致Servlet引擎调用一次Servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HtpServletRequest请求对象和HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,
service方法再根据请求方式分别调用doXXX方法。
Servlet对象的初始化加载(自动加载)
配置Servlet随tomcat服务器启动时 进行初始化 --- <load-on-startup >
那么web应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init方法。(注:实际上调用的是该Servlet对象继承的init(config)方法,但是init(config)方法内部会调用子类覆写的init()方法)
*<load-on-startup > 参数可以是一个数字 0-9 代表服务器加载优先级 0 最高
例如:
1、在tomcat启动时,想通过Servlet加载一些框架配置文件 配置随服务器启动(例如struts1,会在web.xml中配置ActionServlet入口, 客户端发送请求Http Request,被struts1的核心控件器ActionServlet接收,ActionServlet根据struts-config.xml里的映射关系找到对应的Action,调用Action的excute()方法,执行相应的逻辑操作,比如调用Model层的方法,然后通过ActionForward标签,跳转到对应的输出页面,详见:http://blog.csdn.net/liusong0605/article/details/9935329)
2、为web应用写一个InitServlet,这个Servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
结论:
1、编写Servlet 继承HttpServlet2、编写Servlet 不需要覆盖service方法,只需要覆盖doGet和doPost 方法
为什么继承了HttpServlet的Servlet初始化时只需覆盖init() ,无需覆盖init(config) ?
--> GenericServlet.java 中的 init(config)方法
* 原因:HttpServlet继承自GenericServlet,GenericServlet中的init(Config) 调用了 init()* 注意:在启动tomcat时,其调用的是复写的Servlet对象从GenericServlet继承过来的的init(config)方法,并且init(config)方法中会调用HttpServlet子类Servlet对象自己复写的init()方法
public void init(ServletConfig config) throws ServletException
this.config = config;
this.init();
3、当doGet和doPost代码逻辑相同时,可以相互调用,简化编程
Servlet路径映射配置(url-pattern)
一个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优先级:完全匹配 > 目录匹配 > 扩展名匹配
绝对路径和相对路径
路径问题的原因:
结论:客户端/day05/hello 和 服务器内部day05工程中/hello ---- 等价的
路径问题:编写九九乘法表
1、需要用户在客户端输入一个数字2、Servlet接收客户输入数字 打印对应乘法表
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 编写 form 提交数字 -->
<!-- 相对路径 ../chengfabiao -->
<form action="/day05/chengfabiao" method="post">
请输入一个数字<input type="text" name="number"/>
<input type="submit" value="打印乘法表"/>
</form>
</body>
</html>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 编写 form 提交数字 -->
<!-- 相对路径 chengfabiao -->
<form action="/day05/chengfabiao" method="post">
请输入一个数字<input type="text" name="number"/>
<input type="submit" value="打印乘法表"/>
</form>
</body>
</html>
package ustc.lichunchun.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");// 通过 input name 获得数据
int number = 0;
try
number = Integer.parseInt(numberStr);
catch (NumberFormatException e)
//e.printStackTrace();
throw new RuntimeException("输入的不是整数");
// 打印 九九 ---输出到浏览器
PrintWriter out = response.getWriter();
for(int i = 1; i <= number; i++)
for(int j = 1; j <= i; j++)
out.print(i +"*"+ j +"="+ i*j +" ");
out.println("<br/>");
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
doGet(request, response);
在chengfabiao.html 通过 action 访问 ChengfabiaoServlet 路径可以用绝对路径和相对路径
相对路径
相对当前网页地址 路径 例如: chengfabiao ./chengfabiao ../chengfabiao
例如: http://localhost/day05/chengfabiao.html 提交 action="chengfabiao"* 原理:将url最后一个地址换成相对路径
结果: http://localhost/day05/chengfabiao ----- 服务器端 /chengfabiao
* /day05/chengfabiao 与服务器 /chengfabiao 匹配
--------
例如: http://localhost/day05/aaa/chengfabiao.html 提交 action="chengfabiao"
结果: http://localhost/day05/aaa/chengfabiao ----- 服务器 /chengfabiao
* /aaa/chengfabiao 与服务器 /chengfabiao 不匹配 出现404
--------
http://localhost/day05/aaa/chengfabiao.html 提供 action="../chengfabiao"
结果:http://localhost/day05/aaa/../chengfabiao ---- > ..和/aaa抵消 http://localhost/day05/chengfabiao 可以匹配服务器 /chengfabiao
结论:如果用相对路径提交请求,考虑当前路径。 当前访问服务器资源路径不同 ---- 相对路径写法不同
绝对路径
解决 相对路径,会根据当前地址改变问题。 例如: /day05/chengfabiao 、http://localhost/day05/chengfabiao
绝对路径 以/ 开始 --> / 代表访问tomcat服务器根目录(注:这里的绝对路径/ 是对html等来说的,注意和Servlet中的站点根目录/ 区分)
例如: 客户端访问服务器,不管当前路径是什么 --- / 代表服务器根目录(http://localhost)/day05 --- 找到虚拟目录day05工程 /day05/chengfabiao --- 找到 day05工程下配置的虚拟路径/chengfabiao
结论:
1.服务器端和客户端对于/ 的区别
客户端路径 /工程虚拟目录/servlet虚拟路径 例如:/day05/chengfabiao服务器端 配置web.xml 不需要写工程虚拟目录 只要直接写 /servlet虚拟路径 例如:/chengfabiao
(即 客户端的绝对路径 一定要比 服务器端web.xml中配置的路径 多写一个工程名/day05 )
* WebRoot 站点根目录说明:
2.客户端关于路径问题的编程结论
*.html *.jsp内都使用绝对路径
*.css内部使用相对路径----背景图片*.js中使用绝对路径
阶段性总结
上面的知识点概括:
1、掌握Servlet程序编写2、通过路径 访问Servlet 程序
3、Servlet生命周期
接下来围绕着:
Servlet 生命周期来学习init
service
destroy
ServletConfig获得初始化参数
学习init方法 ---- init(ServletConfig) ---- 通过ServletConfig 获得Servlet初始化参数
1、创建一个Servlet2、在web.xml 中 <servlet> 标签内 通过 <init-param> 标签 为Servlet配置初始化参数
<init-param>
<param-name>itcast</param-name>
<param-value>传智播客</param-value>
</init-param>
3、在Servlet程序中通过ServletConfig对象 获得itcast对应数据
getInitParameter ------ 通过name获得value
getInitParameterNames ----- 获得所有name
* 当Servlet配置了初始化参数后,web容器在创建Servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用Servlet的init方法时,将ServletConfig对象传递给Servlet。进而,程序员通过ServletConfig对象就可以得到当前Servlet的初始化参数信息。
package ustc.lichunchun.servlet;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ConfigServlet extends HttpServlet
/*
private ServletConfig config;
// 通过init 方法 初始化
@Override
public void init(ServletConfig config) throws ServletException
// 通过config参数 获得Servlet初始化信息
// 获得指定一个name的value
System.out.println(config.getInitParameter("MyName"));
System.out.println(config.getInitParameter("MySchool"));
// 获得所有name 然后 根据name 获得value
Enumeration<String> names = config.getInitParameterNames();
while(names.hasMoreElements())
String name = names.nextElement();
System.out.println(name +": "+config.getInitParameter(name));
this.config = config;
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
// 获得config 中数据 --- 需要在init方法中 将config对象 保存成员变量
// GenericServlet 已经将ServletConfig 保存成员变量
System.out.println(getServletConfig().getInitParameter("MyName"));
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
doGet(request, response);
* 思考 :如何在doGet 或 doPost 方法中 获得 Servlet初始化参数 ?
将ServletConfig对象保存成 实例成员变量,但其实这件事情,GenericServlet已经帮我们做过了!GenericServlet 已经将ServletConfig 保存成员变量 ----- 在子类中通过 getServletConfig方法 获得 初始化参数
GenericServlet.java
public void init(ServletConfig config) throws ServletException
this.config = config;
this.init();
public ServletContext getServletConfig()
return config;
结论:
子类Servlet不需要覆盖 init(ServletConfig) , 只需要通过GenericServlet中 getServletConfig() 获得ServletConfig对象
应用:
在init-param 指定配置文件位置和名称,配置Servlet随服务器启动创建 load-on-startup
struts配置 ---- 服务器启动时,就会加载ActionServlet(load-on-startup),并且初始化过程中,tomcat自动加载该ActionServlet对应的init-param,封装到ServletConfig对象中。并且由于设置了load-on-startup,tomcat启动时还会调用ActionServlet的init(config),将加载了init-param的ServletConfig传给config,我们在通过getServletConfig方法得到ServletConfig,通过Servletconfig就会读取init-param中name、value,从而读取到struts指定的配置文件的位置。
(注:在调用init(config)方法时,内部还会调用ActionServlet复写的init()方法,该方法就是用来读取struts-config.xml文件中的内容,并根据struts-config.xml中的内容来初始化相关的资源)(即:由于servlet设置了load-on-startup,所以tomcat启动时会加载ActionServlet,也就是会执行ActionServlet中的init()方法,Struts 的初始化实现就是在这里实现的。)
总结:
ServletConfig 配置初始化数据,只能在相对应配置的Servlet中获得,其它Servlet无法获得 ----- 每个Servlet程序都对应一个ServletConfig对象
Web应用对象:ServletContext
ServletContext 是Servlet上下文对象
每一个工程 都会创建 单独ServletContext对象,这个对象代表当前web工程操作ServletContext 必须通过ServletConfig 获得该对象
由于一个web应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。
应用:
1、 获得整个web应用初始化参数2、 实现全局数据共享 (案例--统计站点访问次数)
3、 实现服务器端转发功能 (案例--统计字母出现次数)
4、 读取web工程资源文件
下面我就着重介绍一下这4个方面。
1、ServletContext配置全局参数
ServletContext获取WEB应用的初始化参数 和 ServletConfig 对象的不同:
ServletConfig对象 配置参数,只对当前配置的Servlet有效;
如果配置全局参数,所有Servlet都可以访问,通过ServletContext。
即:ServletConfig每个Servlet对应一个、ServletContext每个工程(web应用)对应一个
package ustc.lichunchun.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();
// 上面写法可以简化一下,因为GenericServlet已经帮我们封装好了
ServletContext context = getServletContext();
// 读取全局初始化参数
System.out.println(context.getInitParameter("hobby"));
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
doGet(request, response);
应用:放一些web工程的版权信息、资源文件、用户密码、管理员邮箱等信息。<context-param>
<param-name>hobby</param-name>
<param-value>唱儿歌</param-value>
</context-param>
2、ServletContext网站访问次数数据共享
通过ServletContext 在多个Servlet间 共享数据
示例:
在ServletContext中 保存站点访问次数 ,每当一个用户访问站点,将访问次数+1
在CountServlet 初始化过程中,向ServletContext 保存访问次数 ---- 0 --------------> ServletContext setAttribute
package ustc.lichunchun.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;
/**
* 用户访问 countservlet 访问次数 +1
*/
public class CountServlet extends HttpServlet
//尽量覆盖无参数的init()方法
@Override
public void init() throws ServletException
// 向ServletContext 保存访问次数 0
// 获得ServletContext对象
ServletContext context = getServletContext();
// 保存数据 setAttribute
context.setAttribute("visittimes", 0);//Attribute类似于一个map表
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
// 每次访问 执行 doGet --- 将visittimes 次数 +1
// 1、从ServletContext中获得 访问次数
ServletContext context = getServletContext();
int times = (Integer)context.getAttribute("visittimes");
// 2、访问次数 +1
times++;
// 3、将访问次数更新回去 ServletContext
context.setAttribute("visittimes", times);
System.out.println("网站被访问了一次!");
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
doGet(request, response);
每次访问次数 +1 --- 数据存放ServletContext中 ---- 所有Servlet都可以获得该数据 ServletContext getAttrbute
* 在ServletContext中保存数据,所有Servlet都可以访问
package ustc.lichunchun.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;
/**
* 通过Servlet查看 网站访问次数
*/
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);
3、ServletContext(转发)统计字母出现次数
通过ServletContext 完成服务器端程序转发
什么是转发? 转发和重定向区别 ?
转发是java特有的一个功能
getServletContext().getRequestDispatcher(java.lang.String path) ---- 完成转发
package ustc.lichunchun.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;
/**
* 分别从AServlet 转发 和重定向 给 BServlet
*/
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/ (即站点根目录)
// 包括后面我们说到的ServletContext读取资源文件,都是在服务器内部进行的
// 这时,我们Servlet文件中使用的/ 代表站点根目录 ---> 见day05图片 站点根目录说明
RequestDispatcher dispatcher = context.getRequestDispatcher("/b");//这里写的和web.xml配置的一致即可
dispatcher.forward(request, response);
*/
// 重定向
// 因为重定向是从客户端访问,所以 / 代表当前web服务器
response.setStatus(302);
response.setHeader("Location", "/day05/b");// 从客户端访问,必须含有工程路径
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
doGet(request, response);
package ustc.lichunchun.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 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);
转发结果:
重定向结果:
* 参数path 代表要跳转的页面的servlet路径,和web.xml中配置的一致即可。
使用转发还是重定向? ---- 转发性能好于重定向,因为请求次数少。
案例:统计字母次数
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 要求用户输入一段内容 -->
<!-- 相对路径 servlet/lettercount -->
<form action="/day05/servlet/lettercount" method="post">
请输入一段英文内容<textarea rows="5" cols="60" name="content"></textarea>
<input type="submit" value="统计"/>
</form>
</body>
</html>
package ustc.lichunchun.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
// 获得用户输入这段英文内容
System.out.println("Letter count ...");
// 获得form 提交内容 content 就是 textarea name属性
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))// c >= 'A' && c <= 'Z'
// 如果 c 是 'A' 在数组 0 位置 +1 ,如果 c是 'B' 在数组1位置+1
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);
request.getParameter("content") 获得form 提交内容 content 就是 textarea name属性。
package ustc.lichunchun.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
// 1 从ServletContext 获得数据
ServletContext context = getServletContext();
int[] times = (int[])context.getAttribute("times");
// 2、将结果打印浏览器
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);
结果如下:
4、ServletContext获得绝对磁盘路径读取web资源文件
利用ServletContext对象读取资源文件
以前我们都是在java工程中读取文件。但是在java工程中读取文件和Servlet中读取文件是有区别的。
现在我分别在day05、day05/src、day05/WebRoot下新建三个txt文件:
复习:
用Java程序如何读取a1.txt、a2.txt、a3.txt --- 默认是读取当前day05工程下文件
package ustc.lichunchun.io;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 复习 同java程序 如何读取 a1.txt a2.txt a3.txt
*/
public class FileReaderTest
public static void main(String[] args) throws Exception
// 默认读取当前工程根目录 /
// a3.txt直接用"a3.txt",因为就在当前工程根目录下
String filename = "src/a1.txt";
readFile(filename);
String filename2 = "WebRoot/a2.txt";
readFile(filename2);
String filename3 = "a3.txt";
readFile(filename3);
private static void readFile(String filename) throws FileNotFoundException,
IOException
BufferedReader in = new BufferedReader(new FileReader(filename));
String line;
while((line = in.readLine()) != null)
System.out.println(line);
in.close();
使用java application 读取文件,可以读取当前工程下所有文件 ----- 使用相对路径读取文件 (绝对路径也可以)
使用Servlet读取文件,只能读取WebRoot下所有文件 ---- 必须使用绝对磁盘路径读取文件(* Servlet读取不到a3.txt,由于Servlet程序只会运行在tomcat环境中、工程只会向tomcat发布WebRoot,故只能读取WebRoot下所有文件)
(* src目录下的文件,在发布工程时,都会提交到WebRoot下的classes文件夹下,所以servlet读取文件可以找到src中对应的a1.txt文件)那web应用如何获取到绝对磁盘路径呢?
在web project工程中,必须通过站点根目录的绝对路径/ 来获得绝对磁盘路径,才能读取WebRoot下的文件----getServletContext().getRealPath(“/WEB-INF/info.txt”)* 因为 WEB-INF/classes 非常特殊 (存放.class文件目录),被类加载器加载,所以还可以通过Class类对象读取 该目录下文件
String filename3 = c.getResource("/a1.txt").getFile(); ----- / 代表 /WEB-INF/classes
package ustc.lichunchun.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;
import sun.reflect.ReflectionFactory.GetReflectionFactoryAction;
/**
* 通过Servlet 读取 文件 -- 必须通过绝对磁盘路径,才能读取web资源文件
*/
public class ReadFileServlet extends HttpServlet
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
// 读取 a1.txt,位于 /WEB-INF/classes
String filename = "/WEB-INF/classes/a1.txt";
filename = getServletContext().getRealPath(filename);
System.out.println(filename);
//E:\\apache-tomcat以上是关于JavaEE实战——Servlet入门Servlet生命周期绝对路径ServletContext的主要内容,如果未能解决你的问题,请参考以下文章
JavaEE实战——会话与状态 Cookie_Session