一、伪代码演示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++; } }