Servlet 是javaweb 三大组件之一
三大组件:
1 Servlet
2 filter
3listener
1.servlet-api 和 javax.servlet-api的区别
在正式查看Servlet源码前,我们首先要分清楚上述两个jar包的区别。
引用:这两个构件都是 Servlet-Specificatoin Jar (Servlet 规范包),只不过因为版本升级:
3.1 之前的 Servlet API 构件叫做 servlet-api-xxx.jar
3.1 及之后的Servlet API 构件改名为 javax.servlet-api-xxx.jar
也就是说两者的区别其实就是版本上的区别,在下载源码包时注意对应的版本。
2.下载源码
据我刚才所说,下载源码时要注意相应的版本。但是从我的实际操作来看,查看servlet-api.jar,使用的是 javax.servlet-api的源码,源码能看问题也不大。
地址:http://www.java2s.com/Code/JarDownload/javax.servlet/javax.servlet-api-3.0.1-sources.jar.zip
3.搭建项目
3.1创建java web项目
3.2查看未引入源码的HttpServlet
3.3选中对应jar包,右键选择Properties
3.4导入下载到的源码jar
3.5导入成功
点击HttpServlet 类名称
例如登录请求找的是登录servlet,注册请求找的是注册servlet,就像每一个话务员一样,每个人 负责的任务都是不一样的,
每一个servlet 负责的请求是不一样的,例如在访问百度的时候,就是大家都在访问一个功能,但是并没有出现站排的现象,所以那就是异步的并发的,会出现线程安全的问题,线程不安全的好处就是快
Servlet概述
特性:
单例,一个类只有一个对象;当然可能存在多个Servlet类!
线程不案例的,所以它的效率是高的!
Servlet类由我们来写,但对象由服务器来创建,并且由服务器来调用相应的方法。
1 什么是Servlet
Servlet是JavaWeb的三大组件之一,它属于动态资源。Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:
接收请求数据(最重要的就接收请求数据,就像是话务员一样,也是先接收你的请求数据);
处理请求,
完成响应。
例如客户端发出登录请求,或者输出注册请求,这些请求都应该由Servlet来完成处理!Servlet需要我们自己来编写,每个Servlet必须实现javax.servlet.Servlet接口。
2 实现Servlet的方式(由我们自己来写!)
实现Servlet有三种方式:
实现javax.servlet.Servlet接口;
继承javax.servlet.GenericServlet类;
继承javax.servlet.http.HttpServlet类(只有这一个类和http请求最接近的,用来处理http请求);
通常我们会去继承HttpServlet类来完成我们的Servlet,但学习Servlet还要从javax.servlet.Servlet接口开始学习。
Servlet.java
publicinterfaceServlet{
public void init(ServletConfigconfig)throwsServletException;
public ServletConfig getServletConfig();
public void service(ServletRequestreq,ServletResponseres)
throws ServletException,IOException;
public String getServletInfo();
public void destroy();
}
一共就是五个方法
在创建servlet 的时候
一定要把这个导入进去,因为tomcat 当中含有servlet-api.jar.
因为servlet 不是jdk 当中的元素,所以采用的api 不是jdk,是
当继承了servlet 接口的时候就自动实现那个5种方法
package cn.itcast.web.servlet;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/*
* 查看Servlet接口中的方法
*/
public class AServlet implements Servlet {
/*
* 它也是生命周期方法
* 它会在Servlet被销毁之前调用,并且它只会被调用一次!
*/
@Override
public void destroy() {
System.out.println("destory()...");
}
/*
* 可以用来获取Servlet的配置信息
*/
@Override
public ServletConfig getServletConfig() {
System.out.println("getServletConfig()...");
return null;
}
/*
* 获取Servlet的信息
*/
@Override
public String getServletInfo() {
System.out.println("getServletInfo()...");
return "我是一个快乐的Servlet";
}
/*
* 它是生命周期方法
* 它会在Servlet对象创建之后马上执行,并只执行一次!(出生之后)
*
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init()...");
/*
* 获取初始化参数
*/
System.out.println(servletConfig.getInitParameter("p1"));
System.out.println(servletConfig.getInitParameter("p2"));
/*
* 获取所有初始化参数的名称
*/
Enumeration e = servletConfig.getInitParameterNames();
while(e.hasMoreElements()) {
System.out.println(e.nextElement());
}
}
/*
* 它是生命周期方法
* 它会被调用多次!!!
* 每次处理请求都是在调用这个方法!
*/
@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
System.out.println("service()...");
}
}
servlet 当中的大部分方法不由我们来调用,由服务器来调用,并且servlet 当中你的对象不由我们来创建由服务器来创建现在我们用的是tomcat 那么就是由tomcat 来创建的,由tomcat 来调用,其实我们写的是片段程序,一些固定不变的是由tomcat 已经写好的,我们写的部分是变化的部分,这两部合在一起才是完整的web 应用
生命周期方法:
void init(ServletConfig):它会在Servlet 对象创建之后马上执行,并只执行一次(出生之后);
voidservice(ServletRequestrequest,ServletResponseresponse):每次处理请求时都会被调用;
void destroy():临死之前(1次):它会在Servlet 被销毁之前调用,并且它只会被调用一次
生命周期的调用过程
还有其他的两种方法
public ServletConfig getServletConfig(); 获取servlet的配置信息
public String getServletInfo(); 获取servlet 的信息,表示的对servlet 的描述信息,一般没什么用。
如何才能用浏览器访问servlet ,浏览器的url 和servlet 如何建立连接呢?
浏览器首先通过url-parttern 写的浏览器的地址,通过浏览器的地址找的Servlet的类
web.xml 配置
<servlet>
<servlet-name>xxx</servlet-name>
<servlet-class>cn.itcast.web.servlet.AServlet</servlet-class>
<init-param>
<param-name>p1</param-name>
<param-value>v1</param-value>
</init-param>
<init-param>
<param-name>p2</param-name>
<param-value>v2</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>xxx</servlet-name>
<url-pattern>/AServlet</url-pattern>
</servlet-mapping>
把服务器启动起来以后通过浏览器访问
http://localhost:8080/day09_1/AServlet
七月 07, 2018 1:37:46 上午 org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
七月 07, 2018 1:37:46 上午 org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 4090 ms
七月 07, 2018 1:37:46 上午 org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
七月 07, 2018 1:37:46 上午 org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.82
七月 07, 2018 1:37:48 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom
WARNING: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [117] milliseconds.
hello --> world!
七月 07, 2018 1:37:48 上午 org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
七月 07, 2018 1:37:48 上午 org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
七月 07, 2018 1:37:48 上午 org.apache.catalina.startup.Catalina start
INFO: Server startup in 2253 ms
init()...
v1
v2
p2
p1
service()...
service()...
service()...
service()... service()方法是一次请求调用一次,n次请求就调用n次,但是init方法是在服务器启动起来以后调用的,所以servlet是在服务器启动以后产生的
servlet 类是我们自己手动创建,但是servlet 对象是服务器创建的,并且servlet里面的方法也是服务器调用的,由于init 是只调用一次,所以可以看出servlet对象是单例模式,在每一个类里面自始至终都只有一个servlet 对象,但是每天有很多的请求访问servlet,所以servlet 是线程不安全的,虽然线程不安全但是效率高
destroy()方法
这么点击停止的时候,服务器会自然停止,相当于关闭了开关,这个时候servlet 会调用destroy()方法
service()...
service()...
service()...
service()...
七月 07, 2018 1:46:16 上午 org.apache.catalina.core.StandardServer await
INFO: A valid shutdown command was received via the shutdown port. Stopping the Server instance.
七月 07, 2018 1:46:16 上午 org.apache.coyote.AbstractProtocol pause
INFO: Pausing ProtocolHandler ["http-bio-8080"]
七月 07, 2018 1:46:16 上午 org.apache.coyote.AbstractProtocol pause
INFO: Pausing ProtocolHandler ["ajp-bio-8009"]
七月 07, 2018 1:46:16 上午 org.apache.catalina.core.StandardService stopInternal
INFO: Stopping service Catalina
destory()...
七月 07, 2018 1:46:16 上午 org.apache.coyote.AbstractProtocol stop
INFO: Stopping ProtocolHandler ["http-bio-8080"]
七月 07, 2018 1:46:16 上午 org.apache.coyote.AbstractProtocol stop
INFO: Stopping ProtocolHandler ["ajp-bio-8009"]
七月 07, 2018 1:46:16 上午 org.apache.coyote.AbstractProtocol destroy
INFO: Destroying ProtocolHandler ["http-bio-8080"]
七月 07, 2018 1:46:16 上午 org.apache.coyote.AbstractProtocol destroy
INFO: Destroying ProtocolHandler ["ajp-bio-8009"]
这个停止相当于突然停电了,不是自然停止,这个时候不会调用destroy 方法,就还和原来一样
INFO: Starting service Catalina
七月 07, 2018 1:37:46 上午 org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.82
七月 07, 2018 1:37:48 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom
WARNING: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [117] milliseconds.
hello --> world!
七月 07, 2018 1:37:48 上午 org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
七月 07, 2018 1:37:48 上午 org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
七月 07, 2018 1:37:48 上午 org.apache.catalina.startup.Catalina start
INFO: Server startup in 2253 ms
init()...
v1
v2
p2
p1
service()...
service()...
service()...
service()...
<url-pattern>/servlet/*[c1] <url-patter>:/servlet/a、/servlet/b,都匹配/servlet/*;
<url-pattern>*.do[c2] </url-pattern>:/abc/def/ghi.do、/a.do,都匹配*.do;
<url-pattern>/*[c3] <url-pattern>:匹配所有URL;
[c1]路径匹配
[c2]扩展名匹配
[c3]啥都匹配
什么是servletConfig对象是服务器创建的,配置对象就是对应的配置文件里面的配置信息,并且每一个配置对象只是针对自己的servlet,每一个servlet 类中都有自己的servletConfig,
ServletConfig Api 中一共就只有这四种方法,所以想获取<servlet-class>是没有办法获取的。
总结
interface Servlet
interface ServletConfig
interface ServletContext 虽然这都是接口但是返回的这些接口的实例化对象都是服务器提供的。
/*
* 获取初始化参数
*/
System.out.println(servletConfig.getInitParameter("p1"));
System.out.println(servletConfig.getInitParameter("p2"));
/*
* 获取所有初始化参数的名称
*/
Enumeration e = servletConfig.getInitParameterNames();
while(e.hasMoreElements()) {
System.out.println(e.nextElement());
}
这是一个神奇的方法,调用完init方法以后,config 对象就有值了,由于config 对象是服务器创建的
Class GenericServlet
模拟genericServlet 里面的方法
public class BServlet implements Servlet {
private ServletConfig config;
/*
* 需要就写,不需要就不写
*/
@Override
public void destroy() {
System.out.println("啊~我要死了!");
}
/*
* 请放心,这个方法一定会在init()方法之后被调用!
* init()被调用后,本类的成员this.config已经有值了!
*/
@Override
public ServletConfig getServletConfig() {
return this.config;
}
/*
* 没有用的东西,爱实现不实现
*/
@Override
public String getServletInfo() {
return "我是一个快乐的Servlet";
}
/*
* 由Tomcat来调用,并且只调用一次
* 它是这些方法中第一个被调用的,它会在构造器之后马上被调用!
*/
@Override
public void init(ServletConfig config) throws ServletException {
// 把tomcat传递的Servletconfig赋值给本类的一个成员,其实就是把它保存起来,方便在其他方法中使用!
this.config = config;
init();
}
/*
* 这个方法是本类自己定义的!不是Servlet接口中的方法
*/
public void init() {
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
/*
* 这里是否可以使用ServletConfig的成员呢?
*/
System.out.println("每次处理请求都会被调用!");
}
public ServletContext getServletContext() {
return config.getServletContext();
}
public String getServletName() {
return config.getServletName();
}
public String getInitParameter(String name) {
return config.getInitParameter(name);
}
}
public class CServlet extends BServlet {
@Override
public void init() {
System.out.println("哇~~~~,很高兴啊~~~");
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
String value = getInitParameter("p1");//调用的是父类的方法
}
}
由于CServlet 继承于BServlet,所以CServlet 在调用Bservlet 中的方法的时候,必须要先有BServlet 的对象才可以,所以只要能调用通,那么服务器就已经创建了BServlet 的对象了,那么BServlet当中的servletConfig 中一定有值了。
请注意
在AServlet extends Servlet 中的service方法是
/*
* 它是生命周期方法
* 它会被调用多次!!!
* 每次处理请求都是在调用这个方法!
*/
@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
System.out.println("service()...");
}
里面的请求和响应是
ServletRequest request, ServletResponse response) 由于这是一个生命周期的方法是很重要的,但是由于我们发送的请求是httpservlet 方法,以后
我们只要在service 生命周期方法中把ServletRequest强制转化为HttpRequest ,ServletResponse 强制转化为HttpResponse ,然后调用service(HttpRequest request,HttpResponse response),由于ServletRequest是HttpServlet 的父接口,ServletResponse是HttpResponse的父接口。
HttpServlet 源码中的service的方法是这样子写的
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 获取ServletContext对象
* 2. 调用其setAttribute()方法完成保存数据
*/
ServletContext application = this.getServletContext();
application.setAttribute("name", "张三");
System.out.println("doGet");
}
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
System.out.println("ServletRequest");
service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
System.out.println("Httpservice");
}
}
后端显示的是
再调用
但是为什么会调用doGet,doPost方法呢,其实不单单有post get 还有delete,PUT,等等
的源码是这样的
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) {
// 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);
}
}
是在service 方法里面调用的doget 或者是dopost 方法。
其实还是先调用的
流程图
public class EServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("doPost");
}
}
<servlet>
<servlet-name>yyy</servlet-name>
<servlet-class>cn.itcast.web.servlet.EServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>yyy</servlet-name>
<url-pattern>/EServlet</url-pattern>
</servlet-mapping>
doget没有重写,少了一个都会报405的错误
由于地址栏是正常访问的没有指定请求方式,所以默认的就是get 请求方式,如果指定请求方式就没有错误了
指定了请求方式以后
点击 提交以后
七月 07, 2018 3:44:55 上午 org.apache.catalina.core.StandardContext reload
INFO: Reloading Context with name [/day09_1] has started
hello --> world!
七月 07, 2018 3:44:55 上午 org.apache.catalina.core.StandardContext reload
INFO: Reloading Context with name [/day09_1] is completed
doPost
doPost 字符串打印出来了
其实在servlet 类创建的时候eclipse 会自动帮你生成好
点击完下一步以后,进入到另外一个页面就直接点击finish
package cn.itcast.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Gservlet
*/
public class Gservlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Gservlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doget");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}
}
web.xml 会自动生成了
<servlet>
<description></description>
<display-name>Gservlet</display-name>
<servlet-name>Gservlet</servlet-name>
<servlet-class>cn.itcast.web.servlet.Gservlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Gservlet</servlet-name>
<url-pattern>/Gservlet</url-pattern>
</servlet-mapping>
<url-pattern>/Gservlet</url-pattern> 可以看出来服务端的路径是/ 代表的项目下面,前端/ 代表的是主机名称
http://localhost:8080/day09_1/Gservlet 默认是get请求
当修改了web.xml 文件以后要重新启动服务器
<servlet>
<description></description>
<display-name>Gservlet</display-name>
<servlet-name>Gservlet</servlet-name>
<servlet-class>cn.itcast.web.servlet.Gservlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Gservlet</servlet-name>
<url-pattern>/Gservlet</url-pattern>
</servlet-mapping>
servlet 与反射
<servlet>
<display-name>Gservlet</display-name>
</servlet>
<servlet-mapping>
<servlet-name>Gservlet</servlet-name>
</servlet-mapping>
这两个标签中的servlet -name 是一样的,
http://localhost:8080/day09_1/Gservlet 首先截取/Gservlet,然后通过web.xml找到/Gservlet,通过匹配<servlet-name>相同找到 <servlet-class>cn.itcast.web.servlet.Gservlet</servlet-class>这个字符串,通过反射
实际上用的是Class c = Class.forName("cn.itcast.web.servlet.Gservlet");
Gservlet gservlet = (Gservlet)c.newIntance(); (保证类里面有无参数的构造方法)得到了gservlet对象 然后调用service 方法 Method c = gservlet.getMethod("service",ServletRuquest.class,ServletReponse.class), c.invoke();调用service 方法,如果不通过反射没有办法实现这种方式,这些步骤都是tomcat 帮助我们做了
Servlet的继承关系
假如现有我们自定义的一个Servlet,继承HttpServlet,那么实际上它的继承链如下图:
可以看到,核心的部分在于:
- 两个顶级接口
- Servlet
- ServletConfig
- 接口的实现类
- GenericServlet
- 基于HTTP协议的实现类
- HttpServlet
我们剥离一下,把不需要看的去掉,再把方法显示一下,可清晰知道Servlet的整体继承关系如下:
其中重点摘录部分进行说明:
- ServletConfig Servlet的配置信息,常用来在Servlet初始化时进行信息传递
- getServletContext() 获取Servlet运行的上下文环境对象,可以获取对应信息(如Servlet路径),存取容量级的变量
- getInitParameter(String name) 获取初始化参数(web.xml中配置的init-param)
- GenericServlet 一般的Servlet,实现了Servlet和ServletConfig接口
- init(ServletConfig config) 初始化方法,方法中调用了init()
- init() 初始化方法,方法体为空,主要用于自定义Servlet的覆盖
- service(ServletRequest request, ServletResponse response) 抽象方法service,要求继承类实现
- destory() Servlet销毁前要执行的方法
- HttpServlet 基于HTTP协议的实现类
- service(ServletRequest request, ServletResponse response) 实现了GenericServlet的抽象方法,调用了service(HttpServletRequest, HttpServletResponse)
- service(HttpServletRequest request, HttpServletResponse response) 根据请求的不同调用了doGet或doPost方法
- doGet() 处理GET方式的请求
- doPost() 处理POST方式的请求
其中稍微提一下,在GenericServlet中有个init(ServletConfig config)方法,调用了init()方法,但是init()方法体却为空,为什么?
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
首先,为了方便能够在其他地方也能直接使用ServletConfig对象,而不仅仅局限在init(ServletConfig config)方法中,所以创建一个私有的成员变量config,在init(ServletConfig config)方法中就将其赋值给config,这样一来,GenericServlet和其子类都可以调用其getServletConfig()方法来获取ServletConfig对象了。
之所以有空的init(),实际上就是为了后续的扩展和重写,有需要的情况下去覆盖init()而不是去覆盖init(ServletConfig config),因为后者一旦覆盖,就无法通过上述的方法在其他地方便捷地调用getServletConfig方法获取ServletConfig对象了。
实际上,Servlet继承关系文字描述起来反而复杂,结合继承图和源码查看,能更加清晰明了,这里进行了大概的阐述,更多细节的话需要查看源码或者API了,此处不再详细展开。
ServletContext(重要),一个项目就一个是唯一的,从哪个方法中获取都是唯一的
一个项目只有一个ServletContext对象!
我们可以在N多个Servlet中来获取这个唯一的对象,使用它可以给多个Servlet传递数据!
这个对象在Tomcat启动时就创建,在Tomcat关闭时才会死去,和tomcat 同生共死,就像是百度服务器一直没停,所以Servlet Context 一直是存活的,命最长。
还有一种对象命比它还长,可以死而复生。
1 ServletContext概述
服务器会为每个应用创建一个ServletContext对象:
ServletContext对象的创建是在服务器启动时完成的;
ServletContext对象的销毁是在服务器关闭时完成的。
ServletContext对象的作用是在整个Web应用的动态资源之间共享数据!例如在AServlet中向ServletContext对象中保存一个值,然后在BServlet中就可以获取这个值,这就是共享数据了。
2 获取ServletContext
ServletConfig#getServletContext();
GenericServlet#getServletContext();
HttpSession#getServletContext()
ServletContextEvent#getServletContext()
在Servlet中获取ServletContext对象:
在void init(ServletConfig config)中:ServletContext context = config.getServletContext();,ServletConfig类的getServletContext()方法可以用来获取ServletContext对象;
在GenericeServlet或HttpServlet中获取ServletContext对象:
GenericServlet类有getServletContext()方法,所以可以直接使用this.getServletContext()来获取;
public class MyServlet implements Servlet {
public void init(ServletConfig config) {
ServletContext context = config.getServletContext();
}
…
}
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletContext context = this.getServletContext();
}
}
域对象的功能(就是在多个servlet中传递数据,域对象必须具备存数据和取数据的功能)
ServletContext是JavaWeb四大域对象之一:
PageContext;
ServletRequest;
HttpSession;
ServletContext;
所有域对象都有存取数据的功能,因为域对象内部有一个Map,用来存储数据,下面是ServletContext对象用来操作数据的方法:
void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“xxx”, “XXX”),在ServletContext中保存了一个域属性,域属性名称为xxx,域属性的值为XXX。请注意,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值,这一特性与Map相同;
Object getAttribute(String name):用来获取ServletContext中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)servletContext.getAttribute(“xxx”);,获取名为xxx的域属性;
void removeAttribute(String name):用来移除ServletContext中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
Enumeration getAttributeNames():获取所有域属性的名称;
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 获取ServletContext对象
* 2. 调用其setAttribute()方法完成保存数据
*/
ServletContext application = this.getServletContext();
application.setAttribute("name", "张三");
}
}
public class BServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 获取ServletContext对象
* 2. 调用其getAttribute()方法完成获取数据
*/
ServletContext application = this.getServletContext();
String name = (String)application.getAttribute("name");
System.out.println(name);
}
}
4 获取应用初始化参数
Servlet也可以获取初始化参数,但它是局部的参数;也就是说,一个Servlet只能获取自己的初始化参数,不能获取别人的,即初始化参数只为一个Servlet准备,就是servletConfig只能是针对自己的servlet。
可以配置公共的初始化参数,为所有Servlet而用!这需要使用ServletContext才能使用!
还可以使用ServletContext来获取在web.xml文件中配置的应用初始化参数!注意,应用初始化参数与Servlet初始化参数不同:
web.xml
<web-app ...>
...
<context-param>
<param-name>paramName1</param-name>
<param-value>paramValue1</param-value>
</context-param>
<context-param>
<param-name>paramName2</param-name>
<param-value>paramValue2</param-value>
</context-param>
</web-app>
<servlet>
<servlet-name>CServlet</servlet-name>
<servlet-class>cn.itcast.servlet.CServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CServlet</servlet-name>
<url-pattern>/CServlet</url-pattern>
</servlet-mapping>
/**
* 演示ServletContext获取公共的初始化参数
* @author cxf
*
*/
public class CServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 得到ServletContext
* 2. 调用它getInitParameter(String)得到初始化参数
*/
ServletContext app = this.getServletContext();
String value = app.getInitParameter("context-param");
System.out.println(value);
}
}
web.xml
<servlet>
<servlet-name>DServlet</servlet-name>
<servlet-class>cn.itcast.servlet.DServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DServlet</servlet-name>
<url-pattern>/DServlet</url-pattern>
</servlet-mapping>
获得真实路径的目的就是当产品卖出去以后不可能还要把里面的路径一一修改,所以不能把路径写成字符串写死。
package cn.itcast.servlet;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 使用ServletContext获取资源路径
* @author cxf
*
*/
public class DServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* /index.jsp 由于是服务端写的,是在java端写的,所以/ 表示的路径是 项目的名称+/index.jsp 然而服务器获取的是服务器上的时间
* jsp html 等等前端写的就是,/ 代表的就是 主机+/index.jsp 前端获取的时间就是自己电脑的时间
*/
//获取的是导入到项目中的index.jsp 文件在电脑上的路径
//F:\JavaEE20140508\apache-tomcat-7.0.42\webapps\day09_2\index.jsp
String path = this.getServletContext().getRealPath("/index.jsp");
System.out.println(path);
/*
* 获取资源的路径后,再创建出输入流对象!
*/
InputStream input = this.getServletContext().getResourceAsStream("/index.jsp");
/*
* 获取当前路径下所有文件的的路径,是一个集合
*/
Set<String> paths = this.getServletContext().getResourcePaths("/WEB-INF");
System.out.println(paths);
}
}
计算网站的访问量
/**
* 统计访问量
* @author cxf
*
*/
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 获取ServletContext对象
* 2. 从ServletContext对象中获取名为count的属性
* 3. 如果存在:给访问量加1,然后再保存回去;
* 4. 如果不存在:说明是第一次访问,向Servletcontext中保存名为count的属性,值为1
*/
ServletContext app = this.getServletContext();
Integer count = (Integer)app.getAttribute("count");
if(count == null) {
app.setAttribute("count", 1);
} else {
app.setAttribute("count", count+1);
}
/*
* 向浏览器输出
* 需要使用响应对象!
*/
PrintWriter pw = response.getWriter();
pw.print("<h1>" + count + "</h1>");
}
}
获取类路径下资源
获取类路径资源,类路径对一个JavaWeb项目而言,就是/WEB-INF/classes下面的文件和/WEB-INF/lib/每个jar包!Class
ClassLoader:
src 下面的所有东西,都会出现在classes 里面
例如在src 下面创建一个a.txt
那么问价会直接出现在classes 里面
由于是webRoot 或者是webContent 下面的文件会发布到服务器当中,java 文件会解析成class 文件解析到WEB-INF /classes 文件中,如果是创建的其他的文件类型会原封不动的复制到classes 文件夹下面去。
类路径实际上就是src下面的文件
往里面导入jar 包要重新发布项目,重新启动服务器。
只要是这块配置了自动部署就没关系,项目会自动部署。
可以同时发布两个项目
因为项目的访问路径是不一样的
a.txt 的路径为("cn/itcast/servlet/a.txt")
b.txt的路径为(“/b.txt”)
index.jsp的路径为("/index1.jsp")
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 得到ClassLoader
* > 先得到Class,再得到ClassLoader
* 2. 调用其getResourceAsStream(),得到一个InputStream
*/
ClassLoader cl = this.getClass().getClassLoader();
// 相对classes 路径 用classLoader a.txt 就是相当于classes路径的 所以a.txt 的路劲就是classes 下面的
"cn/itcast/servlet"
InputStream input = cl.getResourceAsStream("cn/itcast/servlet/a.txt");
String s = IOUtils.toString(input);
System.out.println(s);
//方式1
Class c = this.getClass();
// 相对当前.class文件所在目录!
InputStream input = c.getResourceAsStream("a.txt");
String s = IOUtils.toString(input);//读取输入流内容,转换成字符串返回
System.out.println(s);
用Class c = this.getClass(); 不用ClassLoader ,相对当前.class文件所在目录!
a.txt 和AServlet.class 两个文件路径是并列的关系。
方式二:
相对于classes 文件路径下
Class c = this.getClass();
// 相对当前.class文件所在目录!
// // 相对classes下!
InputStream input = c.getResourceAsStream("/a.txt");
String s = IOUtils.toString(input);//读取输入流内容,转换成字符串返回
System.out.println(s);
Class c = this.getClass();
// “/”表示的相对classes目录下!
// String s = IOUtils.toString(input);//读取输入流内容,转换成字符串返回
InputStream input = c.getResourceAsStream("/../../index.jsp");
“/”表示的就是classes 下面,../ 表示返回上一级,再返回上一级,就到了index.jsp
index.jsp 是和webRoot下面
和上面有区别this.getServletContext().getResourceAsStream("/index.jsp"); 表示的是webRoot 下面的路径,index.jsp 表示的webRoot 下面的,c.getResourceAsStream("/../../index.jsp"); 表示的是classes 下面的。
InputStream input = this.getServletContext().getResourceAsStream("/index.jsp");
InputStream input = this.getServletContext().getResourceAsStream("/index.jsp");
String s = IOUtils.toString(input);
System.out.println(s);
服务器的返回值是:
七月 08, 2018 6:32:31 下午 org.apache.catalina.startup.Catalina start
INFO: Server startup in 561 ms
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
This is my JSP page. <br>
</body>
</html>
七月 08, 2018 6:34:51 下午 org.apache.catalina.core.StandardContext reload
INFO: Reloading Context with name [/day09_4] has started
七月 08, 2018 6:34:51 下午 org.apache.catalina.core.StandardContext reload
INFO: Reloading Context with name [/day09_4] is completed
jsp
写这部分代码的目的:
查看源代码
所以
当在jsp当中写
,写超链接的时候就相当于是
是一个道理