(一)什么是Servlet ???
概念:1、Servlet是SUN公司制定的一套开发动态网页的技术。
2、JavaEE相关的类,包名一般都是以javax开头
servlet规范和服务器的关系
(二)编写第一个Servlet案例应用
1、建立一个标准的JavaWeb应用目录
FirstApp:
WEB-INF:
classes:
lib:
web.xml
2、进入classes目录,建立一个文本文件(所有的Servlet类都必须间接或直接实现javax.servlet.Servlet接口)
FirstApp:
WEB-INF:
classes:
lib:
web.xml
2、进入classes目录,建立一个文本文件(所有的Servlet类都必须间接或直接实现javax.servlet.Servlet接口)
package cn.itcast.servlet;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()); out.close(); }}
3、进入classes目录,对FirstServlet进行编译: 前提:把servlet-api.jar加入到你的构建路径中.set classpath=%classpath%;C:\apache-tomcat-6.0.35\lib\servlet-api.jar 执行:javac -d . FirsetServlet.java
4、修改web.xml,对FirsetServlet进行url地址映射,配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
version="2.5">
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>cn.itcast.servlet.FirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
javaweb项目中,每创建一个servlet都会在web.xml中创建一个对应的servlet节点,方便tomcat进行查找和调用。servlet 节点中
servlet-name值可以随意起名,一般与
servlet-class中的类名保持一致,但要保证值的唯一性,不然在进行映射时会出现问题,
servlet-class节点对应servlet的名称,每个servlet都有一个对应的servlet-mapping节点,两个节点之间通过servlet-name建立关联,
url-pattern的值就是客户端浏览器访问的路径,tomcat根据客户端浏览器访问路径和web.xml中
url-pattern的值进行匹配,如果能匹配到对应的servlet节点,则调用该servlet的service方法,如果没有找到对应的servlet,则返回404错误。
5、把你的应用部署到Tomcat中。
6、访问地址:http://localhost:8080/FirstApp/hello就可以看到写的Servlet类的输出结果了。
(三)servlet的生命周期
servlet的生命周期是由容器控制的,分为init(初始化),service(服务),destory(销毁三个阶段),容器最终要调用service方法为客户进行服务。
Servlet接口中的常用方法:
public void init(ServletConfig config):初始化。Servlet类被实例化后就执行,且执行一次。由容器进行调用public void destroy():销毁Servlet对象。由容器进行调用
在内存中一个Servlet只有一个实例。针对不同的用户请求,容器采用多线程的机制调用service方法的。
Servlet实例对象和初始化方法,默认情况下,只有第一次访问时才执行,且只执行一次。
希望在应用被Tomcat加载完毕后(此时还没有任何人访问),就实例化并完成初始化Servlet的工作?
通过servlet节点下的
load-on-startup节点实现servlet的自启动操作
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>cn.itcast.servlet.FirstServlet</servlet-class>
<load-on-startup>2<load-on-startup>
</servlet>
客户端浏览器请求servlet资源服务器响应流程
(四)servlet与协议相关的子类(HttpServlet)
如果设计与HTTP协议有关的Servlet,一般选择集成javax.servlet.http.HttpServlet.
不要覆盖其中的service(ServletRequest req,ServletResponse resp)方法,而应该覆盖掉,doXXX方法。
doXXX就是根据你的请求方式来的。
HttpServlet中的service方法是典型的模板方法设计模式的具体应用。
(五)在web.xml中配置servlet
1、一个Servlet可以被映射到多个URL地址上
2、URL地址映射还支持通配符*
方式一:以*开头,以扩展名结尾。比如 <url-pattern>*.do</url-pattern>
方式二:以/前缀开头,以*结尾。 比如<url-pattern>/action/*</url-pattern>
3、多个Servlet使用通配符时,有可能有多
以"/"开头(方式二)要比"*"开头(方式一)优先级高
都以"/"开头,还是有多个匹配,找最匹配的
4、如果一个Servlet的映射为一个"/",就称之为默认的Servlet,它负责处理没有映射路径的URL请求的响应。
2、URL地址映射还支持通配符*
方式一:以*开头,以扩展名结尾。比如 <url-pattern>*.do</url-pattern>
方式二:以/前缀开头,以*结尾。 比如<url-pattern>/action/*</url-pattern>
3、多个Servlet使用通配符时,有可能有多
以"/"开头(方式二)要比"*"开头(方式一)优先级高
都以"/"开头,还是有多个匹配,找最匹配的
4、如果一个Servlet的映射为一个"/",就称之为默认的Servlet,它负责处理没有映射路径的URL请求的响应。
(六)servlet的安全性问题
package cn.itcast.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.SingleThreadModel;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo1 extends HttpServlet{
private int num = 0;//servlet计时器
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
num++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
出现原因:由于servlet初始化后在内存中只存在一份,是以单例的模式存在的,多个客户端浏览器访问时是以多线程的方式分别调用service方法,所以如果在调用的service方法中存在类实例变量,可能导致数据不准确,出现多线程问题。
解决方法:1.在service方法中尽量不要使用实例变量,尽量使用局部变量;
2.如果必须使用实例变量,则需要在调用实例变量那块代码上进行加锁(synchronized)操作,且同步代码块尽量包围少的代码。
2.如果必须使用实例变量,则需要在调用实例变量那块代码上进行加锁(synchronized)操作,且同步代码块尽量包围少的代码。