标签:
接下来的三篇博客我会分别介绍Servlet的以下三个方面:
1、Servlet程序编写 ----- 生命周期Servlet的作用:Servlet 用来 动态web资源 开发
静态web资源 : 固定数据文件Servlet技术基于Request-Response编程模型 ---- HTTP协议也是基于请求响应 模型
* Servlet技术 用来 开发基于HTTP协议的 web 应用程序接触 JavaEE API ------ 程序 接口 和 已经实现接口 类的 使用
JavaEE ---- Java Platform, Enterprise Edition 缩写创建步骤:
1、创建web project
2、编写 class 继承 HttpServlet4、覆盖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>|--<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示例:手动编写Servlet运行
1、在webapps 新建 day05test目录 --- 虚拟应用这时,就可以在浏览器中使用 http://192.168.1.104/day05test/hello 来访问这个Servlet程序
编写Servlet程序没有 main函数 ---- tomcat调用Servlet程序执行
Servlet是一个供其他Java程序(Servlet引擎/Tomcat主程序)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
一般开发中,我们是不会手动编写Servlet程序的。
而是通过myeclipse向导 创建Servlet ---- 向导会帮你创建Servlet程序,同时自动生成web.xml 配置
* 生成Servlet信息非常复杂,想生成Servlet 内容整洁一些,精简一些 ------ 修改Servlet模板
1、myeclipse工具 ---- 安装目录 common / plugins在MyEclipse10中设置的,别的版本没测试过!
通过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即可
init(ServletConfig config) 初始化
service(ServletRequest req, ServletResponse res) 提供服务方法,响应客户请求
destroy() 销毁
1、tomcat服务器启动时,没有创建Servlet对象
2、第一次访问时,tomcat构造Servlet对象,调用 init,执行service4、当服务器重启或正常关闭时 调用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对象。
* 但是,在 JavaEE API 中并没有Request和Response实现类 ----- 实现类由Servlet服务器提供的,tomcat提供实现类 weblogic提供实现类
必须阅读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实例对象一旦创建,它就会驻留在内存中,2、在Servlet的整个生命周期内,Servlet的init方法只被调用一次,而且默认是在第一次访问该Servlet程序时才会调用。
而对一个Servlet的每次访问请求都导致Servlet引擎调用一次Servlet的service方法。service方法再根据请求方式分别调用doXXX方法。
配置Servlet随tomcat服务器启动时 进行初始化 --- <load-on-startup >
那么web应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的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
URL 配置格式 三种:
1、完全路径匹配 (以/开始 ) 例如:/hello /init2、目录匹配 (以/开始) 例如:/* /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"结论:如果用相对路径提交请求,考虑当前路径。 当前访问服务器资源路径不同 ---- 相对路径写法不同
解决 相对路径,会根据当前地址改变问题。 例如: /day05/chengfabiao 、http://localhost/day05/chengfabiao
绝对路径 以/ 开始 --> / 代表访问tomcat服务器根目录(注:这里的绝对路径/ 是对html等来说的,注意和Servlet中的站点根目录/ 区分)
例如: 客户端访问服务器,不管当前路径是什么 --- / 代表服务器根目录(http://localhost)结论:
1.服务器端和客户端对于/ 的区别
客户端路径 /工程虚拟目录/servlet虚拟路径 例如:/day05/chengfabiao* 站点根目录说明:
2.客户端关于路径问题的编程结论
*.html *.jsp内都使用绝对路径
*.css内部使用相对路径----背景图片*.js中使用绝对路径
上面的知识点概括:
1、掌握Servlet程序编写接下来围绕着:
Servlet 生命周期来学习init
service
destroy
学习init方法 ---- init(ServletConfig) ---- 通过ServletConfig 获得Servlet初始化参数
1、创建一个Servlet* 当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对象
ServletContext 是Servlet上下文对象
每一个工程 都会创建 单独ServletContext对象,这个对象代表当前web工程应用:
1、 获得整个web应用初始化参数4、 读取web工程资源文件
下面我就着重介绍一下这4个方面。
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>
通过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); } }
通过ServletContext 完成服务器端程序转发
什么是转发? 转发和重定向区别 ?
转发是java特有的一个功能
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); } }
结果如下:
利用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-7.0.42\webapps\day05\WEB-INF\classes\a1.txt readFile(filename); // 读取 a2.txt,位于站点根目录 / String filename2 = "/a2.txt";//--> / 代表WebRoot站点根目录 // getRealPath:通过 / 开始的站点根目录的绝对路径 获得绝对磁盘路径 filename2 = getServletContext().getRealPath(filename2); System.out.println(filename2); //E:\apache-tomcat-7.0.42\webapps\day05\a2.txt readFile(filename2); // 因为a1.txt 位于 /WEB-INF/classes --- 类路径 中 --- 通过Class对象读取文件 Class c = ReadFileServlet.class; // 这条语句中 / 代表classpath 根目录 --- /a1.txt 等价于 /WEB-INF/classes/a1.txt // 返回磁盘绝对路径 String filename3 = c.getResource("/a1.txt").getFile(); System.out.println(filename3);//在国际标准中,所有操作系统的根目录 都默认是 / //--/E:/apache-tomcat-7.0.42/webapps/day05/WEB-INF/classes/a1.txt readFile(filename3); } public 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(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
结论:
在web工程中,必须将 文件路径转换绝对磁盘路径 c:\xxx e:\xxx\xxx ----- getServletContext().getRealPath("/xxx"); / 代表WebRoot 站点根目录
如果读取文件 恰好位于 WEB-INF/classes ----- 通过 类名.class.getResource("/文件名").getFile(); 获得绝对磁盘路径 / 代表 /WEB-INF/classes
如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。
缺省Servlet 功能:处理其他Servlet都不处理的请求tomcat/conf/web.xml org.apache.catalina.servlets.DefaultServlet 作为缺省Servlet
<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet。
我也搞了一个缺省Servlet玩玩:
package ustc.lichunchun.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; /** * 路径 / 这是一个缺省Servlet */ 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);//--/hello.html // 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); } }
这时你也就明白了,静态资源不是自己返回的,是通过缺省的Servlet程序返回的。
JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext
标签:
原文地址:http://blog.csdn.net/zhongkelee/article/details/52262655