Servlet类由我们来写,但对象由服务器来创建,并且由服务器来调用相应的方法。
Servlet是JavaWeb的三大组件之一,它属于动态资源。Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:
实现Servlet有三种方式:
通常我们会去继承HttpServlet类来完成我们的Servlet,但学习Servlet还要从javax.servlet.Servlet接口开始学习。
Servlet.java
publicinterfaceServlet{
publicvoidinit(ServletConfigconfig)throwsServletException;
publicServletConfiggetServletConfig();
publicvoidservice(ServletRequestreq,ServletResponseres)
throwsServletException,IOException;
publicStringgetServletInfo();
publicvoiddestroy();
}
我们开始第一个Servlet应用吧!首先在webapps目录下创建helloservlet目录,它就是我们的应用目录了,然后在helloservlet目录中创建准备JavaWeb应用所需内容:
接下来我们开始准备完成Servlet,完成Servlet需要分为两步:
HelloServlet.java
publicclassHelloServletimplementsServlet{
publicvoidinit(ServletConfigconfig)throwsServletException{}
publicServletConfiggetServletConfig(){returnnull;}
publicvoiddestroy(){}
publicStringgetServletInfo(){returnnull;}
throwsServletException,IOException{
System.out.println("helloservlet!");
我们暂时忽略Servlet中其他四个方法,只关心service()方法,因为它是用来处理请求的方法。我们在该方法内给出一条输出语句!
web.xml(下面内容需要背下来)
在web.xml中配置Servlet的目的其实只有一个,就是把访问路径与一个Servlet绑定到一起,上面配置是把访问路径:"/helloworld"与"cn.itcast.servlet.HelloServlet"绑定到一起。
接下来,我们编译HelloServlet,注意,编译HelloServlet时需要导入servlet-api.jar,因为Servlet.class等类都在servlet-api.jar中。
javac-classpathF:/tomcat6/lib/servlet-api.jar-d.HelloServlet.java
1.1Servlet的出生
服务器会在Servlet第一次被访问时创建Servlet,或者是在服务器启动时创建Servlet。如果服务器启动时就创建Servlet,那么还需要在web.xml文件中配置。也就是说默认情况下,Servlet是在第一次被访问时由服务器创建的。
在Servlet被创建后,服务器会马上调用Servlet的voidinit(ServletConfig)方法。请记住,Servlet出生后马上就会调用init()方法,而且一个Servlet的一生。这个方法只会被调用一次。这好比小孩子出生后马上就要去剪脐带一样,而且剪脐带一生只有一次。
我们可以把一些对Servlet的初始化工作放到init方法中!
1.2Servlet服务
当服务器每次接收到请求时,都会去调用Servlet的service()方法来处理请求。服务器接收到一次请求,就会调用service()方法一次,所以service()方法是会被调用多次的。正因为如此,所以我们才需要把处理请求的代码写到service()方法中!
1.3Servlet的离去
Servlet是不会轻易离去的,通常都是在服务器关闭时Servlet才会离去!在服务器被关闭时,服务器会去销毁Servlet,在销毁Servlet之前服务器会先去调用Servlet的destroy()方法,我们可以把Servlet的临终遗言放到destroy()方法中,例如对某些资源的释放等代码放到destroy()方法中。
1.4测试生命周期方法
publicvoidinit(ServletConfigconfig)throwsServletException{
System.out.println("Servlet被创建了!");
publicvoiddestroy(){
System.out.println("Servlet要离去了!");
在首次访问HelloServlet时,init方法会被执行,而且也会执行service方法。再次访问时,只会执行service方法,不再执行init方法。在关闭Tomcat时会调用destroy方法。
在Servlet接口中还存在三个我们不熟悉的类型:
2.1ServletRequest和ServletResponse(第五天会详细讲解这两个对象)
ServletRequest和ServletResponse是Servlet#service()方法的两个参数,一个是请求对象,一个是响应对象,可以从ServletRequest对象中获取请求数据,可以使用ServletResponse对象完成响应。你以后会发现,这两个对象就像是一对恩爱的夫妻,永远不分离,总是成对出现。
HttpServletRequest方法:
HttpServletResponse方法:
2.1ServletConfig
ServletConfig对象对应web.xml文件中的
ServletConfig对象是由服务器创建的,然后传递给Servlet的init()方法,你可以在init()方法中使用它!
在
在OneServlet中,可以使用ServletConfig对象的getInitParameter()方法来获取初始化参数,例如:
Stringvalue1=servletConfig.getInitParameter("paramName1");//获取到paramValue1
GenericServlet是Servlet接口的实现类,我们可以通过继承GenericServlet来编写自己的Servlet。下面是GenericServlet类的源代码:
GenericServlet.java
publicabstractclassGenericServletimplementsServlet,ServletConfig,
java.io.Serializable{
privatestaticfinallongserialVersionUID=1L;
privatetransientServletConfigconfig;
publicGenericServlet(){}
@Override
publicStringgetInitParameter(Stringname){
returngetServletConfig().getInitParameter(name);
publicEnumeration
returngetServletConfig().getInitParameterNames();
publicServletConfiggetServletConfig(){
returnconfig;
publicServletContextgetServletContext(){
returngetServletConfig().getServletContext();
publicStringgetServletInfo(){
return"";
this.config=config;
this.init();
publicvoidinit()throwsServletException{}
publicvoidlog(Stringmsg){
getServletContext().log(getServletName()+":"+msg);
publicvoidlog(Stringmessage,Throwablet){
getServletContext().log(getServletName()+":"+message,t);
publicabstractvoidservice(ServletRequestreq,ServletResponseres)
publicStringgetServletName(){
returnconfig.getServletName();
在GenericServlet中,定义了一个ServletConfigconfig实例变量,并在init(ServletConfig)方法中把参数ServletConfig赋给了实例变量。然后在该类的很多方法中使用了实例变量config。
如果子类覆盖了GenericServlet的init(StringConfig)方法,那么this.config=config这一条语句就会被覆盖了,也就是说GenericServlet的实例变量config的值为null,那么所有依赖config的方法都不能使用了。如果真的希望完成一些初始化操作,那么去覆盖GenericServlet提供的init()方法,它是没有参数的init()方法,它会在init(ServletConfig)方法中被调用。
GenericServlet还实现了ServletConfig接口,所以可以直接调用getInitParameter()、getServletContext()等ServletConfig的方法。
HttpServlet类是GenericServlet的子类,它提供了对HTTP请求的特殊支持,所以通常我们都会通过继承HttpServlet来完成自定义的Servlet。
HttpServlet类中提供了service(HttpServletRequest,HttpServletResponse)方法,这个方法是HttpServlet自己的方法,不是从Servlet继承来的。在HttpServlet的service(ServletRequest,ServletResponse)方法中会把ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse,然后调用service(HttpServletRequest,HttpServletResponse)方法,这说明子类可以去覆盖service(HttpServletRequest,HttpServletResponse)方法即可,这就不用自己去强转请求和响应对象了。
其实子类也不用去覆盖service(HttpServletRequest,HttpServletResponse)方法,因为HttpServlet还要做另一步简化操作,下面会介绍。
HttpServlet.java
publicabstractclassHttpServletextendsGenericServlet{
protectedvoidservice(HttpServletRequestreq,HttpServletResponseresp)
……
HttpServletRequestrequest;
HttpServletResponseresponse;
try{
request=(HttpServletRequest)req;
response=(HttpServletResponse)res;
}catch(ClassCastExceptione){
thrownewServletException("non-HTTPrequestorresponse");
service(request,response);
在HttpServlet的service(HttpServletRequest,HttpServletResponse)方法会去判断当前请求是GET还是POST,如果是GET请求,那么会去调用本类的doGet()方法,如果是POST请求会去调用doPost()方法,这说明我们在子类中去覆盖doGet()或doPost()方法即可。
publicclassAServletextendsHttpServlet{
publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
System.out.println("hellodoGet()...");
publicclassBServletextendsHttpServlet{
publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
System.out.println("hellodoPost()...");
因为一个类型的Servlet只有一个实例对象,那么就有可能会现时出一个Servlet同时处理多个请求,那么Servlet是否为线程安全的呢?答案是:"不是线程安全的"。这说明Servlet的工作效率很高,但也存在线程安全问题!
所以我们不应该在Servlet中随便创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作。
默认情况下,服务器会在某个Servlet第一次收到请求时创建它。也可以在web.xml中对Servlet进行配置,使服务器启动时就创建Servlet。
在
那么这说明一个Servlet绑定了两个URL,无论访问/AServlet还是/BServlet,访问的都是AServlet。
请注意,通配符要么为前缀,要么为后缀,不能出现在URL中间位置,也不能只有通配符。例如:/*.do就是错误的,因为星号出现在URL的中间位置上了。*.*也是不对的,因为一个URL中最多只能出现一个通配符。
注意,通配符是一种模糊匹配URL的方式,如果存在更具体的
在${CATALINA_HOME}\conf\web.xml中的内容,相当于写到了每个项目的web.xml中,它是所有web.xml的父文件。
每个完整的JavaWeb应用中都需要有web.xml,但我们不知道所有的web.xml文件都有一个共同的父文件,它在Tomcat的conf/web.xml路径。
conf/web.xml
version="3.0">
一个项目只有一个ServletContext对象!
我们可以在N多个Servlet中来获取这个唯一的对象,使用它可以给多个Servlet传递数据!
与天地同寿!!!这个对象在Tomcat启动时就创建,在Tomcat关闭时才会死去!
服务器会为每个应用创建一个ServletContext对象:
ServletContext对象的作用是在整个Web应用的动态资源之间共享数据!例如在AServlet中向ServletContext对象中保存一个值,然后在BServlet中就可以获取这个值,这就是共享数据了。
在Servlet中获取ServletContext对象:
在GenericeServlet或HttpServlet中获取ServletContext对象:
publicclassMyServletimplementsServlet{
publicvoidinit(ServletConfigconfig){
ServletContextcontext=config.getServletContext();
…
publicclassMyServletextendsHttpServlet{
publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse){
ServletContextcontext=this.getServletContext();
ServletContext是JavaWeb四大域对象之一:
所有域对象都有存取数据的功能,因为域对象内部有一个Map,用来存储数据,下面是ServletContext对象用来操作数据的方法:
还可以使用ServletContext来获取在web.xml文件中配置的应用初始化参数!注意,应用初始化参数与Servlet初始化参数不同:
web.xml
...
Stringvalue1=context.getInitParameter("paramName1");
Stringvalue2=context.getInitParameter("paramName2");
System.out.println(value1+","+value2);
Enumerationnames=context.getInitParameterNames();
while(names.hasMoreElements()){
System.out.println(names.nextElement());
5.1获取真实路径(*****)
还可以使用ServletContext对象来获取Web应用下的资源,例如在hello应用的根目录下创建a.txt文件,现在想在Servlet中获取这个资源,就可以使用ServletContext来获取。
5.2获取资源流
不只可以获取资源的路径,还可以通过ServletContext获取资源流,即把资源以输入流的方式获取:
5.3获取指定目录下所有资源路径
还可以使用ServletContext获取指定目录下所有资源路径,例如获取/WEB-INF下所有资源的路径:
Setset=context.getResourcePaths("/WEB-INF");
System.out.println(set);
[/WEB-INF/lib/,/WEB-INF/classes/,/WEB-INF/b.txt,/WEB-INF/web.xml]
注意,本方法必须以"/"开头!!!
一个项目中所有的资源被访问都要对访问量进行累加!
创建一个int类型的变量,用来保存访问量,然后把它保存到ServletContext的域中,这样可以保存所有的Servlet都可以访问到!
相信各位一定见过很多访问量统计的网站,即"本页面被访问过XXX次"。因为无论是哪个用户访问指定页面,都会累计访问量,所以这个访问量统计应该是整个项目共享的!很明显,这需要使用ServletContext来保存访问量。
ServletContextapplication=this.getServletContext();
Integercount=(Integer)application.getAttribute("count");
if(count==null){
count=1;
}else{
count++;
response.setContentType("text/html;charset=utf-8");
response.getWriter().print("
本页面一共被访问"+count+"次!
");application.setAttribute("count",count);
获取类路径资源,类路径对一个JavaWeb项目而言,就是/WEB-INF/classes和/WEB-INF/lib/每个jar包!
这里要讲的是获取类路径下的资源,对于JavaWeb应用而言,就是获取classes目录下的资源。
InputStreamin=this.getClass().getResourceAsStream("/xxx.txt");
System.out.println(IOUtils.toString(in));
InputStreamin=this.getClass().getClassLoader().getResourceAsStream("xxx.txt");