Servlet基础
2.1 HelloWorld
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<font size=30 color=red>Hello World</font><br>");
out.println("<marquee>" + new Date() + "</marquee>");
out.println("</html>");
}
<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/demo/hello.html</url-pattern> </servlet-mapping>
2.2 线程安全Servlet
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
PrintWriter out = resp.getWriter();
count++;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
out.println("<html>");
out.println("This is " + count + " times access" + "The thread name is :" +
Thread.currentThread().getName());
out.println("</html>");
System.out.println("这是第" + count + "次访问" + "线程名称为:"
+ Thread.currentThread().getName());
}
返回结果是:
This is 1times access thread name is :http-8080-1
This is 3times access thread name is :http-8080-1
This is 5times access thread name is :http-8080-2
This is 7times access thread name is :http-8080-3
由上面测试可知,在Servlet中的线程时非安全的,故可以采用以下的方式实现Servlet的线程安全。
2.2.1 继承SingleThreadModel接口
在Servlet中如果继承SingleThreadModel接口那么Servlet引擎将以单线程模式调用service方法。对于实现SingleThreadModel接口的Servlet,Servlet引擎保证其单个实例对象的service方法不会同时被两个线程调用。但是Servlet引擎仍然支持该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象并发运行,但每一个Servlet实例对象的service方法都不会被多个线程并发调用。实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为每个Servlet引擎会创建多个Servlet实例对象,多个并发的请求分别由不同的Servlet实例对象处理或者单个Servlet实例对象排队处理,而真正意义上的多线程安全问题指一个Servlet实例对象被多个线程同时调用的问题。
故在Servlet API 2.4中,SingleThreadModel已经不推荐使用。
2.2.2 编写线程安全的Servlet
注:在Servlet中除了访问成员变量是要注意线程安全问题外,访问其他共享资源是也应注意线程安全问题,如Web应用程序对象、Session对象、数据库等都可能涉及多个Servlet线程同时访问的问题。如果Servlet中没有定义成员变量和service方法中没有访问无同步访问控制的共享数据时,即使是多个线程并发调用service方法,也不会出现线程安全问题。
2.3 ServletConfig接口
Servlet引擎将代表Servlet容器的对象和Servlet的配置参数信息一并封装到一个成为ServletConfig的对象中,并在初始化Servlet实例对象时传递给该Servlet。
Servlet引擎装在并创建Servlet实例对象后,会调用init(ServletConfig config)方法将ServletConfig对象传递给Servlet。
ServletConfig常用方法:
getInitParameterNames()用于获取初始化参数的名称返回一个Enumeration类型
getInitParameter()用于返回初始化参数
getServletName()用于获取Servlet名称
getServletContext()获取Servlet上下文
Web.xml样式:
<servlet> <servlet-name>ServletConfigServlet</servlet-name> <servlet-class>servlet.ServletConfigServlet</servlet-class> <init-param> <param-name>firstname</param-name> <param-value>wang</param-value> </init-param> <init-param> <param-name>lastname</param-name> <param-value>tong</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ServletConfigServlet</servlet-name> <url-pattern>/demo/servletconfig.html</url-pattern> </servlet-mapping>
Java获取ServletConfig参数:
Enumeration e = getServletConfig().getInitParameterNames();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
String value = getInitParameter(key);
}
注:
在Servlet接口中定义了getServletConfig()方法,并在GenericServlet类中实现。用于得到ServletConfig对象。
2.4 GenericServlet&HttpServlet
HttpServlet是GenericServlet的一个提供HTTP协议处理的子类。
2.4.1 Init方法
Init方法在Servlet声明期中仅执行一次,Servlet引擎创建Servlet实例对象后立即调用该方法,通常该方法用户与构造方法类似的初始化功能。
Servlet接口定义的init方法:public void init(ServletConfig config)
GenericServlet在Servlet接口基础上添加一个无参数的init()方法
public void init()该方法没有任何语句,会在init(ServletConfig config)方法的最后一句加以调用、init()方法的目的是为编写初始化内容省去了覆盖init(ServletCOnfig config)方法时编写super.init(config)的麻烦。
2.4.2 Service方法
Service方法时Servlet的核心方法,每当servlet访问请求到达时,Servlet引擎就会调用Servlet实例对象的service方法进行相应。GenericServlet没有实现该方法,在HttpServlet中实现。
2.4.3 Destory方法
在web容器卸装Servlet之前被调用,可以覆盖destory方法完成与init方法相反的功能,释放Servlet打开的资源。
2.4.4 getServletConfig方法
返回Servletconfig对象的引用
2.4.5 doXXX方法
doGet() 处理GET请求
doPost() 处理POST请求
doHead() 处理Head请求
doPut() 处理Put请求
doDelete() 处理DELETE请求
doTrace() 处理TRACE请求
doOptions() 处理OPTIONS请求
2.4.6 浏览器缓存机制
响应信息中的Last-Modified头字段用于指定响应内容的最后更新时间,当客户机缓存此文档内容后,它在以后的请求信息中根据Last-Modified头字段指定的时间来生成If-Modified-Since请求头字段,亦指出缓存文档的最后更新时间。只有文档的更新时间比If-Modified-Since请求头指定的时间新时,服务器才返回文档内容。如果自动If-Modified-Since指定的时间以来,网页内容没有发生修改,服务器将返回一个304状态码表示浏览器缓存的版本是最新的则使用以前缓存的内容。
浏览器根据响应信息中是否包含Last-Modified字段来进行处理,如果响应信息中没有包含Last-Modified字段,将每次访问此页面时都想服务器发出访问请求,否则仅每次启动运行后第一次访问页面才向服务器发出请求,其他都从缓存中读取。
在HttpServlet类定义了一个getLastModified方法,返回值影响浏览器如何使用缓存内容。getLastModified()是一个回调方法,由HttpServlet的service方法调用。Servlet程序调用service()方法后在调用doGet()方法之前,先调用getLastModified()方法,并根据其返回值来决定是都调用doGet方法。规则如下:
1、 getLastModified返回一个负数时,service方法后调用doGet方法。
2、 getLastModified方法返回一个正数且请求中不包含If-Modified-Since,或者If-Modified-Since的时间值比getLastModified方法返回的时间旧时,调用doGet方法。
3、 getLastModified返回一个正数,且请求信息中包含的If-Modified-Since时间比getLastModified返回时间新,则调用浏览器的缓存。
2.4.7 ServletContext接口
每个Web应用程序都是一个独立的Servlet容器,每个Web应用程序分别用一个ServletContext对象。ServletContext对象包含在ServletConfig对象中,调用ServletConfig.getServletContext()方法获取ServletContext对象。
2.4.7.1 获取Web应用程序的初始化参数
<context-param> <param-name>companyName</param-name> <param-value>ibm</param-value> </context-param> <context-param> <param-name>website</param-name> <param-value>ibm.com.cn</param-value> </context-param>
Enumeration<String> e = getServletContext().getInitParameterNames();
while (e.hasMoreElements()) {
String key = e.nextElement();
String value = getServletContext().getInitParameter(key);
}
2.4.7.2 记录日志
ServletContext定义了两个log方法用于记录日志
Public void log(String msg)
Public void log(String message,Throwable t)
2.4.7.3 Application域范围属性
一个Web应用程序中所有Servlet都共享一个ServletContext对象,所以ServletContext对象也是Application对象。
ServletContext定义了4个用于添加、删除、访问application的方法
1、 getAttributeName
2、 getAttribute
3、 removeAttribute
4、 setAttribute
2.4.7.4 访问资源文件
1、 getResourcePath 返回一个包含该目录和文件路径名称的Set集合
2、 getResource 返回映射到资源上的URL对象。
3、 getResourceAsStream 返回连接到某资源上的InputStream对象
PrintWriter out = response.getWriter();
URL url = getServletContext().getResource("/WEB-INF/common.properties");
InputStream is = getServletContext().getResourceAsStream("/WEB-INF/common.properties");
Properties properties = new Properties();
properties.load(is);
out.println("database=" + properties.getProperty("database"));
out.println("username=" + properties.getProperty("username"));
out.println("password=" + properties.getProperty("password"));
2.4.7.5 获取虚拟路径所映射的本地路径
ServletContext接口定义getRealPath()方法用于返回虚拟路径锁映射的本地文件系统路径。