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 继承 HttpServlet 
3、在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("");
		out.println("");
		out.println("");//注意转义字符
		out.println("");
		out.println("");
		out.println("

Hello,这是一个 由Servlet 动态生成网页!

"); out.println(""); out.println(""); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 处理 post方式请求 System.out.println("post 请求 ..."); } }

配置细节

    |--
        |-- 为Servlet程序 命名
        |-- Servlet全路径:包名.类名
    |--
        |-- Servlet 名称

        |-- 用户在浏览器通过/hello 访问Servlet



	
	
		hobby
		电音
	
    
	
		InitServlet
		ustc.lichunchun.servlet.InitServlet
		
		1
  	
  	
		InitServlet
		/init
		/init2
	
  
  	
  
    
  	HelloServlet
  	
  	ustc.lichunchun.servlet.HelloServlet
  
  
    HelloServlet2
    ustc.lichunchun.servlet.HelloServlet2
  
  
    ChengfabiaoServlet
    ustc.lichunchun.servlet.ChengfabiaoServlet
  
  
    
    ConfigServlet
    ustc.lichunchun.servlet.ConfigServlet
    
    
    	MyName
    	李春春
    
    
    	MySchool
    	中国科学技术大学
    
  
  
    ContextServlet
    ustc.lichunchun.servlet.ContextServlet
  
  
    CountServlet
    ustc.lichunchun.servlet.CountServlet
    1
  
  
    CountShowServlet
    ustc.lichunchun.servlet.CountShowServlet
  
  
    AServlet
    ustc.lichunchun.servlet.AServlet
  
  
    BServlet
    ustc.lichunchun.servlet.BServlet
  
  
    LetterCountServlet
    ustc.lichunchun.servlet.LetterCountServlet
  
  
    LetterResultServlet
    ustc.lichunchun.servlet.LetterResultServlet
  
  
    ReadFileServlet
    ustc.lichunchun.servlet.ReadFileServlet
  
  
    DefaultServlet
    ustc.lichunchun.servlet.DefaultServlet
  

  
    
  	HelloServlet
  	
  	/hello
  
  
    HelloServlet2
    /hello2
    /abc/*
    
    *.do
  
  
    ChengfabiaoServlet
    
    /chengfabiao
  
  
    ConfigServlet
    /config
  
  
    ContextServlet
    /context
  
  
    CountServlet
    /count
  
  
    CountShowServlet
    /countshow
  
  
    AServlet
    /a
  
  
    BServlet
    /b
  
  
    LetterCountServlet
    /servlet/lettercount
  
  
    LetterResultServlet
    /servlet/result
  
  
    ReadFileServlet
    /readfile
  
  
    DefaultServlet
    /
  
  
    index.jsp
  

Servlet作用:动态生成 网页文件 

Servlet执行过程:

1、用户在客户端发起url请求 : http://localhost/day05/hello ----- web.xml /hello 映射 HelloServlet程序

2、用户提交请求时,get方式提交 执行 HelloServlet的 doGet方法 post方式提交 执行 HelloServlet的doPost 方法 

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第1张图片

对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程序执行

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第2张图片

Servlet是一个供其他Java程序(Servlet引擎/Tomcat主程序)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度

MyEclipse中Servlet模板修改

一般开发中,我们是不会手动编写Servlet程序的。

而是通过myeclipse向导 创建Servlet ---- 向导会帮你创建Servlet程序,同时自动生成web.xml 配置 

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第3张图片

* 生成Servlet信息非常复杂,想生成Servlet 内容整洁一些,精简一些 ------ 修改Servlet模板

1、myeclipse工具 ---- 安装目录 common / plugins
com.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中设置的,别的版本没测试过!

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第4张图片

理解Servlet继承关系

通过API得到 Servlet 的继承关系

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第5张图片


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()  销毁 

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第6张图片

1、tomcat服务器启动时,没有创建Servlet对象

2、第一次访问时,tomcat构造Servlet对象,调用 init,执行service
3、从第二次以后的访问 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;
	}
}

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第7张图片


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服务器启动时 进行初始化 ---

那么web应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init方法。
(注:实际上调用的是该Servlet对象继承的init(config)方法,但是init(config)方法内部会调用子类覆写的init()方法)

  * 参数可以是一个数字 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应用创建必要的数据库表和数据


    action
    org.apache.struts.action.ActionServlet
    
      config
      /WEB-INF/struts-config.xml
    
    2
 

结论:

1、编写Servlet 继承HttpServlet

2、编写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 init2 in servlet mapping 

2、目录匹配 (以/开始) 例如:/*  /abc/* 

/ 代表网站根目录 

3、扩展名 (不能以/开始) 例如:*.do *.action 

典型错误 /*.do 

优先级:完全匹配 > 目录匹配 > 扩展名匹配

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第8张图片


绝对路径和相对路径

路径问题的原因:

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第9张图片

结论:客户端/day05/hello 和 服务器内部day05工程中/hello ---- 等价的

路径问题:编写九九乘法表

1、需要用户在客户端输入一个数字

2、Servlet接收客户输入数字 打印对应乘法表 





Insert title here




请输入一个数字





Insert title here




请输入一个数字
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("
"); } } 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 站点根目录说明:

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第10张图片

2.客户端关于路径问题的编程结论

  *.html *.jsp内都使用绝对路径

  *.css内部使用相对路径----背景图片

  *.js中使用绝对路径

阶段性总结

上面的知识点概括:

    1、掌握Servlet程序编写
    2、通过路径 访问Servlet 程序
    3、Servlet生命周期

接下来围绕着:

    Servlet 生命周期来学习

        init

        service

        destroy 

ServletConfig获得初始化参数

学习init方法 ---- init(ServletConfig) ---- 通过ServletConfig 获得Servlet初始化参数

1、创建一个Servlet
2、在web.xml 中 标签内 通过 标签 为Servlet配置初始化参数
   
    itcast
    传智播客
   

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 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;
    }

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第11张图片

结论:

子类Servlet不需要覆盖 init(ServletConfig) , 只需要通过GenericServlet中 getServletConfig() 获得ServletConfig对象 

应用:

在init-param 指定配置文件位置和名称,配置Servlet随服务器启动创建 load-on-startup 

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第12张图片

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 的初始化实现就是在这里实现的。)

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第13张图片

总结:

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 对象的不同:

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第14张图片

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工程的版权信息、资源文件、用户密码、管理员邮箱等信息。


	hobby
	唱儿歌	

2、ServletContext网站访问次数数据共享

通过ServletContext 在多个Servlet间 共享数据 

示例

在ServletContext中 保存站点访问次数 ,每当一个用户访问站点,将访问次数+1 

在CountServlet 初始化过程中,向ServletContext 保存访问次数 ---- 0  -------------->  ServletContext  setAttribute 

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第15张图片

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 完成服务器端程序转发 

什么是转发? 转发和重定向区别 ?

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第16张图片

转发是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);
	}

}

转发结果:

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第17张图片

重定向结果:

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第18张图片

* 参数path 代表要跳转的页面的servlet路径,和web.xml中配置的一致即可。

使用转发还是重定向? ---- 转发性能好于重定向,因为请求次数少。

案例:统计字母次数

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第19张图片





Insert title here




请输入一段英文内容
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 + "次!
"); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

结果如下:

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第20张图片

4、ServletContext获得绝对磁盘路径读取web资源文件

利用ServletContext对象读取资源文件

以前我们都是在java工程中读取文件。但是在java工程中读取文件和Servlet中读取文件是有区别的。

现在我分别在day05、day05/src、day05/WebRoot下新建三个txt文件:

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第21张图片

复习:

用Java程序如何读取a1.txt、a2.txt、a3.txt --- 默认是读取当前day05工程下文件

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第22张图片

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的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。

缺省Servlet 功能:处理其他Servlet都不处理的请求 

tomcat/conf/web.xml   org.apache.catalina.servlets.DefaultServlet  作为缺省Servlet 


	default
	org.apache.catalina.servlets.DefaultServlet
	
		debug
		0
	
	
		listings
		false
	
	1

当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet。

JavaEE实战——Servlet入门、Servlet生命周期、绝对路径、ServletContext_第23张图片

我也搞了一个缺省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程序返回的。

总结

1、编写Servlet HelloServlet
2、修改Servlet模板 
3、Servlet生命周期 理论重点掌握
4、Servlet url三种写法 完全、目录、扩展名
5、路径问题:绝对路径 ----- 案例 九九乘法表
将web.xml 配置路径复制到网页 在路径前 /工程名
6、ServletConfig 和ServletContext 读取初始化参数区别 ?
7、ServletContext数据共享案例 ----- 统计访问次数
8、ServletContext转发案例 --- 统计字母出现次数
9、读取web工程中资源文件 ---- 绝对路径
在web工程中,必须将 文件路径转换绝对磁盘路径 c:\xxx e:\xxx\xxx  ----- getServletContext().getRealPath("/xxx");  /代表WebRoot 
如果读取文件 恰好位于 WEB-INF/classes ----- 通过 类名.class.getResource("/文件名").getFile(); 获得绝对磁盘路径  / 代表 /WEB-INF/classes 
10、缺省Servlet 了解功能将静态资源数据内容读取写给客户端 

你可能感兴趣的:(JavaEE)