Servlet的细节问题

1.一个已经注册的Servlet可以被多次映射即:

<servlet>
  <!-- servlet的注册名 -->
  <servlet-name>MyServlet1</servlet-name>
  <!-- servlet类的全路径(包名+类名) -->
  <servlet-class>com.hsp.servlet.MyServlet1</servlet-class>
</servlet>
<!-- 对一个已经注册的servlet的映射 -->
<servlet-mapping>
  <!-- servelt的注册名 -->
  <servlet-name>MyServlet1</servlet-name>
  <!-- servlet的访问路径 -->
  <url-pattern>/MyServlet1</url-pattern>
</servlet-mapping>
<servlet-mapping>
  <servlet-name>MyServlet1</servlet-name>
  <url-pattern>/winner</url-pattern>
</servlet-mapping>

2.当映射一个servlet时候,可以多层 比如

<url-pattern>/servlet/index.html</url-pattern> ok

从这里还可以看出,后缀名是html不一定就是html,可能是假象.

3.把servlet映射到URL中时可以使用*通配符,但是只能有两种方式。

有两种格式:

第一种格式  *.扩展名  比如 *.do,*.ss

第二种格式  以/开头,同时以 /* 结尾.比如/*(所有的请求都跳转到某个地方,如要把整个网站关闭)

/news/*(我们要把门户网站的新闻频道关闭,,所有对新闻频道的访问都跳转到某个页面)

通配符练习题:

Servlet1 映射到 /abc/*

Servlet2 映射到 /*

Servlet3 映射到 /abc

Servlet4 映射到 *.do

问题(面试题)

当请求URL为"/abc/a.html","/abc/*"和"/*"都匹配,哪个servlet响应

Servlet引擎将调用Servlet1

当请求URL为"/abc"时,"/abc/*"和"/abc"都匹配,哪个servlet响应

Servlet引擎将调用Servlet3

当请求URL为"/abc/a.do"时,"/abc/*"和"*.do"都匹配,哪个servlet响应

Servlet引擎将调用Servlet1

当请求URL为"/a.do"时,"/*"和"*.do"都匹配,哪个servlet响应

Servlet引擎将调用Servlet2

当请求URL为"/xxx/yyy/a.do"时,"/*"和"*.do"都匹配,哪个servlet响应

Servlet引擎将调用Servlet2

在匹配的时候,要参考的标准:

(1)看谁的匹配度高,谁就被选择

(2)*.do 的优先级最低

实际应用:现在我们要把整个网站关闭,我们自己写一个servlet,如CloseServlet,向页面输出out.println(‘对不起,该网站暂时关闭’)。然后配置web.xml:

<servlet>
  <servlet-name>CloseServlet</servlet-name>
  <servlet-class>com.....CloseServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>CloseServlet</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping> 

对这个网站的所有请求都由CloseServlet处理。

将原来web.xml拷贝一份,重命名为web.xml.bak,网站恢复之后再改回来!

4.Servlet单例问题

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

  在servlet的整个生命周期中,servlet的init()方法只会被调用一次,而对一个servlet的每次访问请求都会导致servlet引擎调用一次servlet实例的service()方法,对于每次访问请求,servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给他调用的servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。

当Servlet被第一次访问后,就被加载到内存,以后该实例对各个请求服务.即在使用中是单例.

因为Servlet是单例,因此会出现线程安全问题: 比如:售票系统. 如果不加同步机制,则会出现问题:

怎么证明是单例呢?

public class MyServlet1 extends HttpServlet {
    int i = 0;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.doPost(request, response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        i++;
        response.getWriter().println("hello, i="+i);
    }
}

i的值会一直增加!多个请求增加的是同一个i。

售票可能出现的问题:

public class MyServlet1 extends HttpServlet {
    int ticket = 2;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.doPost(request, response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        if(ticket > 0){
            System.out.println("你买到票了");
            try {
                //线程休眠用来模拟成千上万个人同时并发,因为一个售票系统会有很多人同时访问!
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //先休眠再减!!买完票还没来得及减另一个人就来买了!
            ticket--;
        }else{
            System.out.println("你买不到票了");
        }
    }
}

解决:

(1)如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制

synchronized (对象){
   //同步代码
}
public class MyServlet1 extends HttpServlet {
    int ticket = 2;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.doPost(request, response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        synchronized (this) {
            if(ticket > 0){
                System.out.println("你买到票了");
                try {
                    //线程休眠用来模拟成千上万个人同时并发,因为一个售票系统会有很多人同时访问!
                    Thread.sleep(10 * 1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //先休眠再减!!买完票还没来得及减另一个人就来买了!
                ticket--;
            }else{
                System.out.println("你买不到票了");
            }
        }
    }
}

(2)如果一个变量不需要共享,则直接在doGet()或者doPost()定义,这样不会存在线程安全问题.

public class MyServlet1 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        int j = 0;
        j ++;
        response.getWriter().println("j=" + j);
    }
}

无论谁请求j的值都是1.

5.servlet 中的 <load-on-startup> 配置

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

需求: 当我们的网站启动的时候,可能会要求初始化一些数据,(比如创建临时表), 在比如:

我们的网站有一些要求定时完成的任务[ 定时写日志,定时备份数据.. 定时发送邮件..]

解决方法: 可以通过 <load-on-startup> 配合线程知识搞定.

先说明<load-on-startup>: 通过配置<load-on-startup> 我们可以指定某个Servlet 自动创建.

<load-on-startup>1</load-on-startup>

这个1是指servlet被初始化的顺序。

对于一个大型的网站来说,初始化的servlet可能不止一个,它们的相对执行顺序用数字表示

任务:web应用启动时创建一个后台线程,定时完成某些任务(每隔十秒发一封邮件)。

我们来模拟一个定时发送电子邮件的功能:这个功能应该在网站启动的时候就开始

实现思路:

sendEmailTable

id                  content                 sendtime

1                  "hello"                   2011-11-11 20:11

2                   "hello2"                2012-11-11 10:00

public class SendEmailThread extends Thread{
    @Override
    public void run() {
        int i=0;
        try {
            //这个事应该在一直执行。
            while(true){
                //每休眠一分钟,就去扫表sendmail, 看看那份信件应当被发出
                Thread.sleep(10*1000);
                System.out.println("发出 第"+(++i)+"邮件");//javamail
            }
        } catch (Exception e) {
            e.printStackTrace();
            // TODO: handle exception
        }
    }
}
public class MyInitServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public void init() throws ServletException {
        // Put your code here
        System.out.println("MyInitServlet1 的init被调用..");
        //完成一些初始化任务
        System.out.println("创建数据库,表,读取参数");
        //创建一个线程
        SendEmailThread sendEmailThread=new SendEmailThread();
        sendEmailThread.start();
    }
}

ServletConfig对象

该对象主要用于 读取 servlet的配置信息.

<servlet>
    <servlet-name>ServletConfigTest</servlet-name>
    <servlet-class>com.hsp.servlet.ServletConfigTest</servlet-class>
    <!-- 这里可以给servlet配置信息,这里配置的信息,只能被该servlet 读取 -->
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
</servlet>

如何使用

String encoding = this.getServletConfig().getInitParameter("encoding");
response.setCharacterEncoding(encoding);

servlet可以指定编码方式

补充说明:这种配置参数的方式,只能被某个Servlet独立使用.如希望让所有的Servlet都去读取某个参数,这样配置:

<!-- 如果这里配置参数,可被所有servlet读取 -->
 <!--
 <context-param>
 <param-name></param-name>
 <param-value></param-value>
 </context-param>
-->
如果要把所有的参数都读取,则使用 如下方法 :
Enumeration<String> names=this.getServletConfig().getInitParameterNames();

while(names.hasMoreElements()){
  String name=names.nextElement();
  System.out.println(name);
  System.out.println(this.getServletConfig().getInitParameter(name));
}

Servlet的一些细节问题的更多相关文章

  1. servlet中的细节

    Get方法有大小限制:1024个字符.这些信息使用 Query_String头传递,并通过Query_String环境变量访问.Post方法:请求体信息使用FromData头传递.读取所有表单参数:g ...

  2. Servlet的一些细节(2)

    1. Servlet的创建时间 Servlet是不能单独运行,调用它的叫做Servlet引擎,或者叫做web服务器 针对客户端的多长Servlet请求,通常情况下,服务器只会创建一个Servlet实例 ...

  3. Servlet的一些细节(1)

    1.  Servlet程序必须映射到一个URL地址 由于客户端是通过URL访问web服务器资源,所以Servlet程序必须映射到一个URL地址.这个工作在web.xml文件中使用<servlet ...

  4. [Java.Web] Servlet 的一些细节

    本文来自 传智播客视频PPT 1. 由于客户端是通过 URL 地址访问 web 服务器中的资源,所以 Servlet 程序若想被外界访问,必须把 servlet 程序映射到一个 URL 地址上,这个工 ...

  5. Servlet的一些细节

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

  6. 2016-2-1 Servlet细节

    Servlet的一些细节(韩顺平老师视频讲解)(1)由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序想要被外界访问,必须把servlet程序映射到一个URL地址上.这个工作在 ...

  7. JavaWeb -- Servlet运行过程 和 细节

    Servlet的运行过程 lServlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后: ①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象.如果是,则 ...

  8. Servlet映射细节

    Servlet的映射细节: 1):一个Servlet程序(Web组件),可以配置多个<url-pattern>,表示一个Servlet有多个资源名称. <servlet-mappin ...

  9. java web学习总结(五) -------------------servlet开发(一)

    一.Servlet简介 Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向 ...

随机推荐

  1. 在java中HttpServletResponse响应中文出现乱码。

    以字符串的形式输出. 1.response.getWriter().write("您好中国hello"); 如果这样输出的话.则浏览器结果为: 2.加上代码 response.se ...

  2. 一些常用sqlite语句

    1,如果表不存在就新建一个 CComBSTR bstrCreatBat(L”CREATE TABLE IF NOT EXISTS tb_Name (\ rowIdIndex  INTEGER PRIM ...

  3. Junit的最简单样例:Hello world!

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3824934.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...

  4. linux端口与进程命令

    1 lsof命令 lsof -i:portNum 查出来与该端口相关的所有程序 2 netstat命令: netstat -lnp

  5. DEDECMS中,channelartlist标签

    当前频道的下级栏目的内容列表 dede:channelartlist 标签: {dede:channelartlist row=6} <dl> <dt><a href=' ...

  6. 关于fputs和fgets的几个细节

    C语言中两个标准IO fputs和fgets都是针对行来进行数据的读取的!这里关于这两个IO函数我有几个小细节想在这里和大家分享一下,希望能够对大家产生帮助! 首先贴上这两个函数的函数声明,下面以这两 ...

  7. 巧用Systemtap注入延迟模拟IO设备抖动

    原创文章,转载请注明: 转载自系统技术非业余研究 本文链接地址: 巧用Systemtap注入延迟模拟IO设备抖动 当我们的IO密集型的应用怀疑设备的IO抖动,比如说一段时间的wait时间过长导致性能或 ...

  8. foreach的原理

    1.foreach 语句对实现 System.Collections .IEnumerable 或 System.Collections.Generic .IEnumerable <T > ...

  9. 【转】分享10VPN

    以下介绍的vpn,都是有免费流量赠送的免费vpn,完全不免费的不在之列. 免费vpn因为用的人比较多,所以高峰时段可能会有点慢,但是人少时,还是比较顺畅的.对于偶尔浏览外网,看看新闻的同学来说,免费v ...

  10. C++ STL中迭代器失效的问题

    my_container.erase(iter); 其中my_container是STL的某种容器,iter是指向这个容器中某个元素的迭代器.如果不是在for,while循环中,这种方式删除元素没有问 ...