Servlet的生命周期以及线程安全问题

Servlet的基础体系

在这里插入图片描述

Servlet的运行过程

Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:

1,Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第4步,否则,执行第2步。

2,装载并创建该Servlet的一个实例对象。

3,调用Servlet实例对象的init()方法。

4,创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。

5,WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

Servlet生命周期

/**
 * Servlet的声明周期:
 *  Servlet01() - 调用1次(服务器查询是否有该Servlet对象,没有就创建 --- 单例)
 *  init(ServletConfig config) - 调用1次(初始化数据的方法)
 *  根据请求类型调用doGet()或doPost方法 - 前端发送1次请求就调用1次
 *  destroy() - 销毁方法,服务器销毁,该Servlet才会结束
 */
第一步:Servlet构造函数
 public Servlet01() {
        System.out.println("Servlet01构造方法");
    }
第二步:init(ServletConfig config)
@Override
    public void init(ServletConfig config) throws ServletException {
        //获取匹配信息(配置信息可以在web.xml 或者 在注解里配置)
        String code = config.getInitParameter("code");
        System.out.println(code);
        System.out.println("初始化 init方法");
    }
第三步:调用doGet()/doPost()
//前端发送GET请求,服务器就就会调用doPost方法
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);

    }
    //前端发送Post请求,服务器就会调用doPost()
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("接受到请求");
    }
第四步:调用destroy()
@Override
    public void destroy() {
        System.out.println("Servlet01销毁方法");
    }
service()和doGet()和doPost()方法的区别
/*1,service()既能处理get请求又能处理post请求,如果servlet中有service方法,会执行service()
2,doGet()只能处理get请求
3,doPost()只能处理post请求
注意:三个方法同时存在的情况下 首先执行srvice()
*/
方式一:web.xml
    <servlet>
        <servlet-name>Servlet01</servlet-name>
        <servlet-class>com.ll.servlet.Servlet01</servlet-class>
        <!--init-param 是初始化参数-->
        <init-param>
            <!--是参数名-->
            <param-name>code</param-name>
            <!--是参数值-->
            <param-value>UTF-8</param-value>
        </init-param>
          <!-- 在项目启动的时候会被创建
            注意:数字>=0,数字越小,优先级越高
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
<!--servlet-mapping 标签给 servlet 程序配置访问地址-->
    <servlet-mapping>
        <!--servlet-name 标签的作用是告诉服务器,我当前配置的地址给哪个 Servlet 程序使用-->	
        <servlet-name>Servlet01</servlet-name>
        <!--url-pattern 标签配置访问地址 <br/> 
		/ 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 <br/> 
		/Servlet01 表示地址为:http://ip:port/工程路径/Servlet01 <br/> -->
        <url-pattern>/Servlet01</url-pattern>
    </servlet-mapping>
</web-app>
方式二 注解
/**
* @WebServlet - Servlet的注解
*      value - url连接
*      initParams - 初始化参数(数组)
*            @WebInitParam 初始化参数注解
*                  name - 参数名
*                  value - 参数值
*
*/
@WebServlet(value = "/Servlet02",
        initParams = {
                @WebInitParam(name = "code", value = "UTF-8")
        },
        loadOnStartup = 1
)
public class Servlet02 extends HttpServlet {
    public Servlet02() {
        System.out.println("Servlet02构造方法");
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        String code = config.getInitParameter("code");
        System.out.println(code);
        System.out.println("初始化 init方法 Servlet02");
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);

    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet02接受到请求");
    }
    @Override
    public void destroy() {
        System.out.println("Servlet02销毁方法");
    }
}
面试题
 /*  面试题:Servlet何时被创建?
 *      1.前端发送请求,服务器会查看是否有该Servlet对象,没有就创建
 *      2.在web.xml中配置<load-on-startup>1</load-on-startup> 或者 在注解中配置 loadOnStartup = 1
 *          Servlet会在项目启动时被创建(注意:数组>=0,数字越小,优先级越高)
 */

ServletConfig对象

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

​ Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。

​ Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对

象。

ServletConfig作用

1、可以获取 Servlet 程序的别名 servlet-name 的值

2、获取初始化参数 init-param

3、获取 ServletContext 对象

<!--init-param 是初始化参数-->
<init-param>
    <!--是参数名-->
     <param-name>code</param-name>
    <!--是参数值-->
     <param-value>UTF-8</param-value>
 </init-param>
@Override public void init(ServletConfig servletConfig) throws ServletException {
    System.out.println("2 init 初始化方法"); 
    // 1、可以获取 Servlet 程序的别名 servlet-name 的值 
    System.out.println("HelloServlet 程序的别名是:" + servletConfig.getServletName()); 
    // 2、获取初始化参数 init-param 
    System.out.println("初始化参数 code 的值是;" + servletConfig.getInitParameter("code"));
    // 3、获取 ServletContext 对象 
    System.out.println(servletConfig.getServletContext());
     }

线程安全问题

出现原因

多个客户端访问同一个Servlet中的资源时,有可能会出现线程安全问题

出现问题案例
@WebServlet("/Servlet3")
public class Servlet03  {
    private int num;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        num++;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        resp.getWriter().println(num);
    }
}
解决方案一

1.将Servlet实现SingleThreadModel(已过时),因为当线程阻塞,就会创建新的Servlet对象

@WebServlet("/Servlet3")
public class Servlet03 extends HttpServlet implements SingleThreadModel {
    private int num;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        num++;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        resp.getWriter().println(num);
    }
}
解决方案二

利用线程锁机制, synchronized或lock

@WebServlet("/Servlet3")
public class Servlet03 extends HttpServlet   {
    private int num;
    private Lock lock=new ReentrantLock();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        lock.lock();
        num++;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        resp.getWriter().println(num);
        lock.unlock();
    }
}
面试题
/**
 * 面试题:Servlet是单例吗?
 *  不一定,如果Servlet实现了SingleThreadModel接口,
 该Servlet线程阻塞时,前端发送请求会再创建该Servlet的对象
 */

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值