(转)Servlet——开发细节+ServletConfig对象

Servlet 一些细节问题

(一)于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把Servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成

(二)<servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name><servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。

(三)一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name><url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。

  1. ★这里需要注意的是,一个servlet可以被多次映射,也即一个Servlet可以有多个<servlet-mapping>,对外提供多个访问路径。举例如下:

[html]  view plain  copy
  1. <servlet>  
  2.     <servlet-name>MyServlet1</servlet-name>  
  3.     <servlet-class>com.gavin.servlet.MyServlet1</servlet-class>  
  4.     </servlet>  
  5.   
  6.     <servlet-mapping>  
  7.         <servlet-name>MyServlet1</servlet-name>  
  8.         <url-pattern>/MyServlet1</url-pattern>  
  9.     </servlet-mapping>  
  10.     
  11.     <servlet-mapping>  
  12.         <servlet-name>MyServlet1</servlet-name>  
  13.         <url-pattern>/Gavin.html</url-pattern>  
  14. </servlet-mapping>  
  15.   
  16. <servlet-mapping>  
  17.         <servlet-name>MyServlet1</servlet-name>  
  18.         <url-pattern>/servlet/Gavin.html</url-pattern>  
  19.  </servlet-mapping>  

这里可以看到,我们将MyServlet1映射成了一个名字为MyServlet1,另一个名字为Gavin.html,还有一个名字为/servlet/Gavin.html。这里也得出结论:

★后缀名为html的资源不一定真的就是html

★映射的名字可以有多级,有多个斜杠


      ★在Servlet映射到的URL中也可以用*通配符,但只能有两种固定的格式,一种格式是“*.扩展名”,另一种是以正斜杠(/)开头并以”/*”结尾。

[html]  view plain  copy
  1. <servlet-mapping>  
  2.         <servlet-name>AnyName</servlet-name>  
  3.         <url-pattern>/*</url-pattern>  
  4.     </servlet-mapping>  
  5.   
  6. <servlet-mapping>  
  7.         <servlet-name>AnyName</servlet-name>  
  8.         <url-pattern>/news/*</url-pattern>  
  9. </servlet-mapping>  
  10.   
  11. <servlet-mapping>  
  12.         <servlet-name>AnyName</servlet-name>  
  13.         <url-pattern>*.do</url-pattern>  
  14. </servlet-mapping>  

则第一种可以匹配任何名字的servlet,第二种可以匹配前面有一层为newsservlet,这种方法可以应用在:比如一个网站要对新闻版块进行整改,则可以用这种方法将新闻版块的请求指向其他地方,将其暂时关闭。第三种匹配后缀为doservlet


匹配原则:在匹配的时候,(1)看谁的匹配度高,谁就被选中;(2)【*.后缀名】的优先级最低,实在没有匹配项的时候才匹配这个。


看一个面试题:


对于如下的一些映射关系:

  • Servlet1映射到/abc/*

  • Servlet2映射到/*

  • Servlet3映射到/abc

  • Servlet映射到*.do

  1. 当请求URL为“/abc/a.html”,“/abc/*”和”/*”都匹配,哪个Servlet响应?

    答案:Servlet1

  2. 当请求URL为“/abc”时,”/abc/*””/abc””/*”都匹配,哪个Servlet响应?

    答案:Servlet3

  3. 当请求URL为“/abc/a.do”,“/abc/*”和” *.do”都匹配,哪个Servlet响应?

    答案:Servlet1

  4. 当请求URL为“/a.do”,“/*”和”*.do”都匹配,哪个Servlet响应?

    答案:Servlet2à*.后缀名】的优先级最低

5、当请求URL为“/xxx/yyy/a.do”,“/*”和” *.do”都匹配,哪个Servlet响应?


答案:Servlet2à还是*.后缀名】的优先级最低


通配符的价值在于可以在网站某版块或者整个网站进行维护时,进行暂时的关闭版块或网站。


(四)Servlet是一个供其他java程序(Servlet引擎)调用的java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度


(五)针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,(单例模式)也就是说Servlet实例一旦创建,它就会驻留在内存中,为后续的其他请求服务,直至web容器退出/或者reloadweb运用servlet实例对象才会销毁。

      Servlet单例模式存在线程安全问题,要注意并发处理,比如买票系统,票是所有客户端共享的,那么售票的代码应该使用同步机制。

      synchronized(this){


}

如果一个变量不是共享的,就不需要设置为成员变量,只需要在doGet或者doPost方法中定义为局部变量即可。


(六)在Servlet的整个生命周期内,Servletinit方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servletservice方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,service方法再根据请求方式分别调用doXXX方法。


(七)★如果在<servlet>元素中配置了一个<load-on-startup>元素,那么web应用程序启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。

      用途:为web应用写一个InitServlet,这个Servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。或者是启动一个后台线程,定时去完成某些工作(比如每隔10秒发一封电子邮件)

      举例:

      新建ServletInitServlet,其代码如下:

[java]  view plain  copy
  1. package com.gavin.servlet;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.PrintWriter;  
  5.   
  6. import javax.servlet.ServletException;  
  7. import javax.servlet.http.HttpServlet;  
  8. import javax.servlet.http.HttpServletRequest;  
  9. import javax.servlet.http.HttpServletResponse;  
  10.   
  11. public class InitServlet extends HttpServlet {  
  12.       
  13.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  14.             throws ServletException, IOException {  
  15.   
  16.     }  
  17.   
  18.       
  19.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  20.             throws ServletException, IOException {  
  21.   
  22.         this.doGet(request, response);  
  23.     }  
  24.   
  25.     //初始化函数  
  26.     public void init() throws ServletException {  
  27.         System.out.println("InitServlet的init函数被调用...");  
  28.         System.out.println("创建库、表...或者定时任务...");  
  29.     }  
  30.   
  31. }  

其中只写了init函数的代码。在web.xml部署如下,其不用<servlet-mapping>映射,因为它只要在启动web应用时调用,而不需要外部访问


[html]  view plain  copy
  1. <servlet>  
  2.     <servlet-name>InitServlet</servlet-name>  
  3.     <servlet-class>com.gavin.servlet.InitServlet</servlet-class>  
  4.     <!-- 这里的1表示所有自启动的servlet的启动顺序,可以用1、2、3... -->  
  5.     <load-on-startup>1</load-on-startup>  
  6.   </servlet>  

这里的1表示所有自启动的servlet的启动顺序,可以用123...

启动Tomcat服务器,可以看到:



启动Tomcat服务器的同时,这个Servlet已经被装载到内存了。


这里我们来模拟一个定时发送电子邮件的功能,实现思路:

这里当用户设定了一个定时发送邮件的任务后,数据库里肯定存在一张表,大概如下:


Id

Content

sendTime

1

Hello

2014-5-18 00:00

2

Happy Birthday

2014-5-20 13:14

这里只是做一个简单的模拟,新建一个sendMainThread线程类

[java]  view plain  copy
  1. package com.gavin.model;  
  2.   
  3. public class SendMailThread extends Thread {  
  4.     public void run() {  
  5.         int i = 0;  
  6.         while (true) {  
  7.             try {  
  8.                 // 每休眠一分钟,就去扫表sendmail,看看哪些信件应当发送  
  9.                 Thread.sleep(1000 * 10);  
  10.                 System.out.println("发出 第" + (++i) + "封信件");  
  11.             } catch (Exception e) {  
  12.                 e.printStackTrace();  
  13.             }  
  14.         }  
  15.     }  
  16. }  

InitServlet中创建该线程,并启动即可

[java]  view plain  copy
  1. public void init() throws ServletException {  
  2.         System.out.println("InitServlet的init函数被调用...");  
  3.         System.out.println("创建库、表...或者定时任务...");  
  4.         //创建一个线程  
  5.         SendMailThread sendMailThread = new SendMailThread();  
  6.         sendMailThread.start();  
  7.     }  

效果:


当然真实的情况应该是要比对数据库中的时间的。


大型的网站会有多个自启动的Servlet,这里要用123….表示启动的顺序。前面也提到了!


ServletConfig对象

      ◆在Servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。

      ◆当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。


如下:

[html]  view plain  copy
  1. <servlet>  
  2.     <servlet-name>MyServlet1</servlet-name>  
  3.     <servlet-class>com.gavin.servlet.MyServlet1</servlet-class>  
  4.     <!-- 这里可以给servlet配置信息,这里的配置信息,只能被该servlet读取 -->  
  5.     <init-param>  
  6.         <param-name>encoding</param-name>  
  7.         <param-value>utf-8</param-value>  
  8.     </init-param>  
  9.   </servlet>  

说明:这里的配置参数方法,只能被该servlet读取,而不能被其他servlet使用

Servlet程序中,可以读取出设置参数,如下:

[java]  view plain  copy
  1. response.setCharacterEncoding(this.getInitParameter("encoding"));  

如果有多个参数,可以使用getInitParameterNames()方法,该方法返回枚举类型Enumeration,可以通过for循环拿到对应的参数名称,然后通过参数名称再通过上述方法拿到参数,当然也可以一个个拿到。

◆当然,还有一种全局的参数设置,<context-param>节点也可以配置初始化参数,并且可以被所有servlet读取

猜你喜欢

转载自blog.csdn.net/Admin_QF/article/details/80748803