1.Servlet属于JavaEE的部分,所以JavaSE的文档是没有Servlet API的。JavaEE API又包括很多其他技术,找一个专门的Servlet API文档。
2.Servlet程序运行在服务器端,由服务器调用,所以先在服务器端建立一个Web应用,根据Web应用的组织结构,把java程序放在webapps\workday10\WEB-INF\classes目录中。
3.导包,查阅Servlet API,编写测试程序:
package cn.itcast;
import java.io.*;
import javax.servlet.*;
//服务器端运行程序,客户程序请求服务器,服务器调用
public class FirstServlet extends GenericServlet
{
public void service(ServletRequest req,
ServletResponse res)
throws ServletException,
java.io.IOException
{
OutputStream out=res.getOutputStream();
out.write("hello servlet!!!".getBytes());
}
}
直接编译的结果:
D:\Program Files\apache-tomcat-7.0.69\webapps\workday10\WEB-INF\classes>javac Fi
rstServlet.java
FirstServlet.java:4: 错误: 程序包javax.servlet不存在
import javax.servlet.*;
^
FirstServlet.java:7: 错误: 找不到符号
public class FirstServlet extends GenericServlet
^
符号: 类 GenericServlet
FirstServlet.java:9: 错误: 找不到符号
public void service(ServletRequest req,
^
符号: 类 ServletRequest
位置: 类 FirstServlet
FirstServlet.java:10: 错误: 找不到符号
ServletResponse res)
^
符号: 类 ServletResponse
位置: 类 FirstServlet
FirstServlet.java:11: 错误: 找不到符号
throws ServletException,
^
符号: 类 ServletException
位置: 类 FirstServlet
5 个错误
原因:Servlet相关的包不在Java环境的JavaSE API中,要正确编译程序,需要将Servlet API相关jar包所在目录加入到classpath当中(编译器到classpath中寻找Class文件),而Tomcat服务器可以运行Servlet,其中必有Servlet的jar包(想想Eclipse编程,那些第三方jar包,同样要导入到所写程序环境路径下),故将其jar包所在目录加入到classpath中重新编译:
D:\Program Files\apache-tomcat-7.0.69\webapps\workday10\WEB-INF\classes>set clas
spath=D:\Program Files\apache-tomcat-7.0.69\lib\servlet-api.jar
D:\Program Files\apache-tomcat-7.0.69\webapps\workday10\WEB-INF\classes>javac -d
. FirstServlet.java
D:\Program Files\apache-tomcat-7.0.69\webapps\workday10\WEB-INF\classes>
5.在该应用的web.xml中为Servlet配置对外访问路径(借鉴Tomcat的示例):
FirstServlet
cn.itcast.FirstServlet
FirstServlet
/FirstServlet
一个Servlet完成。
6.启动Tomcat服务器,用浏览器访问:
http://localhost:8080/workday10/FirstServlet
结果:
hello servlet!!!
Eclipse开发Servlet
Tomcat启动时会加载所有的应用,上面的程序是手写,用jdk1.6编译的,而即将用Eclipse开发的Servlet设置成了jdk1.5(运行),故Tomcat会加载jdk1.6编译,导致编译环境版本高于运行环境,所以要删除上面那个Web应用。
1.准备工作:设置服务器----->Window--->Preferences-->Servers--Tomcat-->Tomcat6.x导入本机Tomcat服务器所在目录,注意一定设置成Enabled,可选JDK版本,仍然注意运行环境要高于编译环境。
2.建立一个Web工程workday10,此为一个Web应用的实际根目录,在Eclipse创建工程的选项卡中,workday10内部的目录Web root folder实际保存着该Web应用的组织结构,Context root URL为该Web应用的虚拟目录映射,所以实际访问时访问/workday10/xxx.注意下面还有JavaEE环境的选择(导入JavaEE的许多jar包)
3.在src目录下建立Servlet程序,带上包名,直接继承GenericServlet,实现service方法,注意参数出现问题是因为没有导入用到的JavaEE的源码,去找apache-tomcat-6.0.45-src文件夹,tomcat源码自带用到的JavaEE的源码。导入后重新实现该方法,参数正常。
4.部署启动服务器,注意选择第一步配置的服务器版本,Eclipse启动服务器,仍然选择第一步配置的版本。
5.配置(练习用,以后可用Eclipse自动配置)WEB_INF目录下的web.xml文件:
ServletDemo1
cn.itcast.ServletDemo1
ServletDemo1
/ServletDemo1
index.jsp
6.开启浏览器,访问:
http://localhost:8080/workday10/ServletDemo1
结果:
Hello,Servlet...
访问成功。
Servlet调用过程和生命周期
客户端访问Servlet流程图:
(注:关于创建Servlet对象,只在第一次访问这个Servlet时才创建,此后一直驻留在内存中,直到web服务器停止或删除此应用;向客户机输出的数据是先写到response对象中,再由服务器取出发送给浏览器)
Servlet生命周期:第一次访问时Servlet对象创建,调用init方法,处理服务器请求调用service方法,此后一直驻留在内存中不再重新创建,destroy方法在web服务器停止时,或把此应用删除时执行,摧毁Servlet对象。(经典面试题,还有可能询问所有其他和web服务相关对象的生命周期)
HttpServlet:复写了service方法,其内容是判断处理各种类型的用户请求,其中我们常用的只有doGet,doPost,所以我们继承了HttpServlet后,一般几乎不会复写service方法,而只复写doGet,doPost方法。
一些重要细节:
1.改错误的类名------>不在代码中改,用重构功能,这样其他程序也跟着改;
2.在Eclipse中改类模板----->去MyEclipse根目录搜索servlet.java文件,改成自动生成自己想要的样子;
3.注意同一个应用下不同Servlet的url-pattern;拷贝工程时,需要在Properties-->MyEclipse-->Web中改Web Context-root;
4.用多个servlet-mapping将同一个Web资源映射成多个url地址,比如1.html-->伪静态;
5.服务器会自动监测web.xml的改动,所以只改动web.xml不需要重新发布--->在服务器的context.xml中配置WatchedResource,让所有的web应用自动加载;
6.Servlet对象只创建一次,而有多少次客户端请求就调用多少次service方法,创建多少个ServletRequest,ServletResponse对象
7.url-pattern的/*与*xxx,通配符匹配,找长的最像的,*xxx优先级低于所有其他模式
8.缺省Servlet:处理其他Servlet都不处理的请求,将url-pattern配置成/
!注意:访问所有web资源包括静态资源,实际都是访问Servlet(这也是前面虚拟目录映射的原理),比如1.html,服务器会先寻找有没有哪个Servlet映射到这个地址,如果没有,调用默认缺省Servlet在web应用目录下查找这个资源,如果有则调出这个资源返给浏览器,如果你自己配置缺省Servlet,则覆盖掉默认的,将所有其他Servlet不处理的请求映射到自己的缺省Servlet.举例:
直接在web目录下建立一个1.html,访问:
http://localhost:8080/workday10/1.html
这是服务器的自动虚拟目录映射,实际就是调用默认缺省Servlet.
在应用的web.xml中配置自己的缺省Servlet:
ServletDemo3
/servlet/ServletDemo3
ServletDemo3
/
这时访问:
http://localhost:8080/workday10/servlet/ServletDemo3
正常显示结果:
Hello,Servlet Still Here...
访问:
http://localhost:8080/workday10/1.html
服务器调用你的缺省Servlet,结果:
Hello,Servlet Still Here...
随意访问其他不存在的页面也是一样:
http://localhost:8080/workday10/dgdeggegerge
结果:
Hello,Servlet Still Here...
服务器默认缺省Servlet在其/Conf/web.xml中设置,被所有应用加载:
default
org.apache.catalina.servlets.DefaultServlet
debug
0
listings
false
1
1
映射:
default
/
!不要设置自己的缺省Servlet,这样你的服务器中所有的静态资源别人都无法访问了!(懂原理一带而过)
线程安全问题:
多个客户端并发访问同一个Servlet,Web服务器会为每个客户端请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就可能产生线程安全问题。
举例:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int i=0;
i++;
System.out.println(i);
}
int i=0;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
i++;
System.out.println(i);
}
还要注意的就是static字段,比如一个static集合可能导致内存溢出!每次添加完数据后要记得移除数据!
测试多线程:
int i=0;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
i++;
//睡10秒
try {
Thread.sleep(1000*10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
response.getOutputStream().write((i+"").getBytes());
}
解决:放入同步代码块中,但在实际中根本不能用---多用户访问网页!
Servlet规范中解决方式:implements SingleThreadModel标记接口,访问冲突时创建另一个Servlet提供服务。