首页 > 代码库 > Servlet(2)

Servlet(2)

一、伪代码演示Tomcat的内部代码运行

1)、通过映射找到servlet-class的内容,字符串:com.gqx.servlet.FirstServlet

2)、通过反射构造构造FirstServlet对象

  2、1 得到字节码(class)文件对象

  Class clazz=class.forName("com.gqx.servlet.FirstServlet");

  2、2调用无参的构造方法来构造对象

  Object obj =clazz.newInstance();  -->serlvet的构造方法被激活

3)、创建ServletConfig对象,通过反射调用init方法

   3.1 得到方法对象

                  Method m = clazz.getDeclareMethod("init",ServletConfig.class);

          3.2 调用方法

                  m.invoke(obj,config);             --2.servlet的init方法被调用

4)创建request,response对象,通过反射调用service方法

          4.1 得到方法对象

                  Methodm m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);

           4.2 调用方法

      m.invoke(obj,request,response);  --3.servlet的service方法被调用  

 5)当tomcat服务器停止或web应用重新部署,通过反射调用destroy方法

           5.1 得到方法对象

                  Method m = clazz.getDeclareMethod("destroy",null);

            5.2 调用方法

                  m.invoke(obj,null);            --4.servlet的destroy方法被调用

用时序图来演示servlet的生命周期

技术分享

Servlet的自动加载

  默认情况下,第一次访问servlet的时候创建servlet对象。如果servlet的构造方法或init方法中执行了比较多的逻辑代码,那么导致用户第一次访问sevrlet的时候比较慢。         改变servlet创建对象的时机: 提前到加载web应用的时候!!!

         在servlet的配置信息中,加上一个<load-on-startup>即可!!

如下

<servlet>

    <servlet-name>LifeDemo</servlet-name>

    <servlet-class>gz.itcast.c_life.LifeDemo</servlet-class>

    <!-- 让servlet对象自动加载 -->

    <load-on-startup>1</load-on-startup> 
<!-- 注意: 整数值越大,创建优先级越低!!-->

  </servlet>

有参的init方法和无参的init方法

  有参的init方法中实现了无参的init方法,其源代码中调用了this.init();所以我们一般都会在无参的init方法中写入代码

Servlet的多线程并发问题

  

注意: servlet对象在tomcat服务器是单实例多线程的。

因为servlet是多线程的,所以当多个servlet的线程同时访问了servlet的共享数据,如成员变量,可能会引发线程安全问题。

 技术分享

解决办法:

         1)把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)

         2)建议在servlet类中尽量不要使用成员变量。如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围。(哪里使用到了成员变量,就同步哪里!!),以避免因为同步而导致并发效率降低。

 

实例代码如下:

package com.gqxing.servlet2;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ThreadDemo extends HttpServlet {
    
    /**
     * 多线程安全问题
     * 案例:访问网站的第几个人数
     */
    int count=1;
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("你现在是当前网站的第"+count+"个访客!");
        //为了效果,这里用sleep方法去让线程同步
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //当多个线程同时访问这里获取相同的count值的时候会发生线程安全问题。
        count++;
    }

}

效果如图

技术分享

这个时候要加入避免出现线程安全的机制

package com.gqxing.servlet2;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ThreadDemo2 extends HttpServlet {
    
    /**
     * 多线程安全问题
     * 案例:访问网站的第几个人数
     */
    int count=1;
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        //为当前访问的线程加锁,
        synchronized (ThreadDemo2.class) {//锁线程必须是唯一的,可以用当前的字节码对象
            response.getWriter().write("你现在是当前网站的第"+count+"个访客!");
        }    
        count++;
    }

}

 

Servlet(2)