Servlet
Servlet是sun公司提供的一种用于开发动态web资源的技术
为了方便web服务器对动态web资源的管理,sun公司在其api中定义了一个Servlet接口
服务器针对接口进行调用,我们编写的应用程序针对接口进行实现
习惯性地,我们把实现了Servlet接口的java类统称为Servlet
1. javaweb 学什么
用 java 语言开发动态的web资源,接下来就是介绍如何开发动态的web资源
对于java程序员而言,所谓动态web资源就是可以运行在服务器上的java程序
2. 实现服务器管理java程序
开发人员写好一个java类,到底有哪些方法tomcat服务器是不可能知道的
tomcat服务器需要执行我们编写的java类就需要知道我们的java类有哪些方法,然后在适当的时间调用这些方法, 所以我们在写的java程序要想运行在服务器上就必须要实现一个特殊的接口 Servlet.java
interface Servlet { ... }
Servlet 接口中就定义了可以被tomcat服务器调用的java方法
通常来讲,我们将实现了Servlet接口的java类称之为 Servlet
编写好的Servlet需要web.xml文件中做配置才能供外界访问
3. 实现第一个Servlet程序
3.1 写一个java类实现Servlet接口
package cn.itcast.servlet;
import java.io.*;
import javax.servlet.*;
public class HelloWorldServlet extends GenericServlet
{
// 实现 service 方法
public void service(ServletRequest request,ServletResponse response)
throws ServletException,java.io.IOException {
// 向浏览器输出一句话
PrintWriter out = response.getWriter();
out.write("hello world!!!");
}
public void init()throws ServletException {
// 初始化 servlet 时被调用
System.out.println("init()");
}
public void destroy() {
// 摧毁 servlet 时被调用
System.out.println("destroy()");
}
}
3.2. 导入 servlet jar包
set classpath=%classpath%;D:apache-tomcat-6.0.20libservlet-api.jar 3.3. 编译带包的类
javac -d . HelloWorldServlet.java
3.4. 将包拷贝至 day05/WEB-INF/classes 目录下 --> 发布 web 工程
3.5. 在 web.xml 文件中做映射
<!-- 做servlet映射 -->
<!-- servlet元素用于给一个类起别名 -->
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-class>cn.itcast.servlet.HelloWorldServlet</servlet-class>
</servlet>
<!-- servlet-mapping元素用于将一个Servlet映射到url -->
<!—url必须以/开头,/ 表示当前web应用即上下文路径 -->
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/HelloWorldServlet</url-pattern>
</servlet-mapping>
注意: servlet 对象一旦创建就会驻留在内存中,为所有的请求服务,什么时候销毁
直到服务器关闭时或web应用被移除才销毁
3.6. Servlet 执行流程图
4. 用 eclipse 工具开发 Servlet
4.1. 建一个 web project
4.2. 在src下建包,创建一个java类实现Servlet接口
4.3 在 WebrootWEB-INFweb.xml 做 servlet 映射
4.4 配置 tomcat 服务器
window--> preferences --> tomcat6.x
4.5 将web工程发布至tomcat 服务器
发布的web应用名称可以配置: web工程右键 选properties-->myeclipse-->web
默认情况使用工程名作为发布后的web应用名
4.6 启动tomcat服务器运行程序
5. HttpServlet对象
一般来讲我们开发一个Servlet会去继承 HttpServlet
在 eclipse 下开发Servlet 可以直接新建一个Servlet, 覆写 HttpServlet 的 doGet和doPost方法
继承 HttpServlet 的原因是: HttpServlet实现了service方法,将ServletRequst和ServletResponse
强转为子类 HttpServletRequest和HttpServletResponse,让我们用起来更加方便,同时,在service方法中,它判断了请求方式,根据请求方式来调用 doGet 和 doPost
Servlet 细节
1. * 号统配符
一个Servlet可以映射为多个路径
在映射 Servlet 路径时可以使用‘/*’ 或 ‘*.扩展名’ 的形式
注意: 两者不能同时使用
/* 具有较高的优先级
2. load-on-startup 元素
<servlet>元素下可以配置< load-on-startup>子元素,
配置方式如下:
<load-on-startup>1</load-on-startup>
如果一个Servlet配置了该项,web容器会在web应用被加载时就初始化该Servlet,数字越小则越先初始化
3. tomcatconfweb.xml
服务器下所有web 应用中的web.xml 都会自动继承该文件中所有的配置
http://localhost:8080/day05/a.html a.html是资源名
上面的url访问的url在web.xml文件中并没有配置
此时会去访问缺省的Servlet,在tomcatconfweb.xml文件中就配置了一个缺省的DefaultServlet DefaultServlet帮我们去web应用下读取 a.html 文件,并打给浏览器,如果没有发送 404 页面
也就说,我们通过ie访问服务器访问的都是 Servlet
4. Servlet线程安全问题
解决Servlet线程安全问题: 加上同步的锁(lock)
实现SingleThreadModel接口的Servlet
服务器会做判断,当有请求过来,如果Servlet对象忙着呢,服务器会再创建一个Servlet对象为用户
提供服务,如果Servlet闲置,就直接提供服务
这样的方式实际上是回避了线程安全问题, 单线程访问Servlet, 这样的方式不可取
ServletConfig 对象
作用 : 封装 Servlet 初始化参数
1. 可以在 web.xml 文件中 Servlet 元素下 为Servlet配置初始化参数
<init-param>
<param-name>name</param-name>
<param-value>aaaa</param-value>
</init-param>
2. web 容器在初始化Servlet时,会将初始化参数封装到一个 ServletConfig 对象中,传给init方法
3. 我们在Servlet 中覆写 init方法,就可以获得ServletConfig
4. 父类 GenericServlet 中定义了一个成员变量用于记住此对象,并提供了 getServletConfig 方法
我们可以直接调用此方法 获得 config 对象
5. 再调用 getInitParameter(name) 方法获得想要配置项
// 指定编码
// 获得ServletConfig 对象
ServletConfig config = getServletConfig();
String encoding = config.getInitParameter("encoding");
System.out.println("encoding=" + encoding);
ServletContext 对象
1. ServletContext对象代表整个web应用
2. ServletContext对象是一个域对象(可以存储数据的对象)
ServletContext对象的内部维护了一个map集合, key是String类型 value是Object类型
class ServletContext {
private Map<String, Object> map ;
}
通常来讲,我们会将全局的数据存入 ServletContext 域对象
3. ServletContext 作为域对象, 多个Servlet 可以共享数据
Servlet6
// 1. 获得ServletContext 对象
ServletContext context = getServletContext();
// 2. 存入域
context.setAttribute(“name”, “zhangsan”);
Servlet7
// 获得 context 域, getAttribute
String name = (String) getServletContext().getAttribute("name");
4.获取web应用的初始化参数
getContext().getInitParameter(“name”);
5. 统计一个web应用的访问量
在 context 域中维护一个count变量
访问Servlet时,取出变量 加1
6. 实现请求转发
实现请求转发需要用到 转发对象 RequestDispatcher
有一个 forward 方法能转发请求
7. 如何读取工程中的文件
7.1. 读取web工程下的资源文件
// 获得绝对路径
String realPath = ServletContext.getRealPath(相对web应用的路径);
注意URL url = ServletContext.getResource(); web的url
// 获得与文件关联的流
InputStream in=
ServletContext.getResourceAsStream(“WEB-INF/classes/config.properties”;
7.2 读取java工程下的文件
// 不能相对虚拟机目录 不能用绝对路径
// 只能类加载的方式读
// 获得 流
ClassLoader classLoader = Demo.class.getClassLoader();
InputStream in = classLoader.getResourceAsStream("a.txt");
// 获得绝对路径
URL url = Demo.class.getClassLoader().getResource("a.txt");
String path = url.getPath();
类加载方式缺点
1) 不能读取类路径以外文件
2) 由于需要加载到内存,不能读大文件
web工程中如果用类加载的方式读
类加载实际上读取的是内存中加载的文件,此时将读不到硬盘上资源文件的修改
解决办法Demo.class.getClassLoader().getResource("a.txt").getPath()
通过绝对路径去读硬盘上的文件 避开内存的文件
Servlet缓存
HttpServlet 的 Service方法中的代码
// 调用方法
long lastModified = getLastModified(req);
// 如果为 -1 ,就直接放行,给最新的
if (lastModified == -1) {
doGet(req, resp);
}
// 方法返回不是-1 else {
// 读取IE发送的头If-Modified-Since
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
// 方法的返回值大于ie发送过来的时间头
// 重新向浏览器发送了一个时间头
maybeSetLastModified(resp, lastModified);
// 放行, 发送页面
doGet(req, resp);
} else {
// 方法的返回值没有大于ie发送过来的时间头
// 发送 304 状态码,让用户去读缓存
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}