JAVA Web知识点整理(五)
Servlet的类层次结构
(1)Servlet接口---init()方法 service()方法 destroy()方法,这三个核心方法构成了Servlet组件的生命周期
(2)ServletConfig接口---getInitParameter()方法---获取局部初始化参数
(3)GenericServlet类---普通Servlet---为Servlet接口和ServletConfig接口中的方法提供了默认实现
这里体现缺省适配器设计模式
(4)HttpServlet类---专门用于Http协议下的Servlet---继承GenericServlet类,并提供了doGet(),doPost()等6个方法等待程序员在自定义的Servlet类中重写
Servlet的生命周期
(1)生命周期是组件在不同时间处于的不同状态,Servlet的生命周期是指Servlet组件在Web容器中从产生到销毁的完整过程
(2)Web容器负责Servlet组件的加载/实例化(new) --> 初始化(init) --> 服务(service) --> 销毁(destroy)
其中加载、初始化、销毁只会调用一次,服务每次请求时都会被调用
Servlet的加载时机
(1)首次访问Servlet时(第一个客户首次请求时),容器会自动实例化Servlet,并调用init()方法
(2)如果<servlet>标签中配置了<load-on-startup>子标签,则会在Web容器启动时,自动加载
<load-on-startup>N</load-on-startup> //这里的N是大于等于0的整数 值越小,越先加载
Servlet的销毁时机
(1)服务器停止或重启时
(2)Servlet类的源代码被更新时
控制台输出---INFO: Reloading Context with name [/项目名] has started,表示项目源代码更新后,重新被服务器加载
Servlet的运行机制---单实例多线程
(1)单实例---为了提高效率,每个Servlet只有一个实例,在第一次请求时被实例化;
(2)多线程---只要有客户发出请求,就创建出一个线程负责响应客户的请求,其service()方法被多个用户反复调用,直到其被销毁
解决Servlet产生的线程安全问题
(1)原因
因为Servlet是单实例的,所以必然是以多线程的方式处理多个客户的请求,这样当多个客户同时读写Servlet对象中的成员变量时,就可能出现线程安全问题
(2)解决办法
(2.1)使用同步代码块控制对成员变量的访问
(2.2)不在Servlet类中使用成员变量,只使用service()方法中的局部变量 推荐
Servlet组件在web.xml文件中的配置
<servlet> <servlet-name>Servlet的名称</servlet-name> <servlet-class>Servlet的完整类名</servlet-class> </servlet> <servlet-mapping> <servlet-name>Servlet的名称</servlet-name> <url-pattern>Servlet的URL映射模式</url-pattern> </servlet-mapping>
Servlet的完整执行流程
(1)容器根据访问的完整路径URL截取出项目名后面的ServletPath作为url-pattern
(2)根据此url-pattern到web.xml文件中查找是否存在和此url-pattern匹配的<url-pattern>标签,如果找不到,则报404错误,否则找到对应的<servlet-name>子标签
(3)根据<servlet-name>的值去匹配<servlet>标签中的<servlet-name>子标签,进而找到<servlet-class>子标签,最终得到Servlet的完整类名
(4)根据Servlet的完整类名,判断Servlet的实例是否已存在,如果不存在就创建出单一的实例,并执行init()方法,相当于懒汉式单例
如果<servlet>标签中配置了<load-on-startup>子标签,则在服务器启动时,就创建出单一的实例,并执行init()方法,相当于饿汉式单例
这里体现单例设计模式
(5)对于客户的每一次请求,容器都会创建一个单独的线程来处理,同时创建出HttpServletRequest请求对象和HttpServletResponse响应对象,并将其作为参数传到service()方法中,service()方法内部根据具体的请求方法去调用doGet()或doPost()方法,service()方法执行结束后,服务器自动删除request对象和response对象,并将结果返回给客户端
这里体现模板方法设计模式
(6)Servlet被重新加载时,或服务器停止或重启时,调用destroy()方法,并销毁Servlet实例
service()方法和doGet(),doPost()方法的关系
(1)service()方法在HttpServlet父类中已经给出定义,根据不同的请求的方法去调用doGet()或doPost()方法,体现模板方法设计模式
因此,一般情况下,只重写doGet()和doPost()方法,不要重写service()方法(因为会导致破坏了模板方法结构的设计)
(2)如果必须重写service()方法,需要在方法中手工调用super.service(request, response);
局部初始化参数---只能被当前Servlet读取到的参数
(1)定义局部参数---在<servlet>标签下面添加<init-param> <servlet> <servlet-name>...<servlet-name> <servlet-class>...<servlet-class> <init-param> <param-name>局部参数名</param-name> <param-value>局部参数值</param-value> </init-param> </servlet> (2)读取局部参数 this.getInitParameter("局部参数名");
全局初始化参数---能被项目中的所有Servlet读取到的参数
(1)定义全局参数---在所有<servlet>标签之外添加<context-param>标签 <context-param> <param-name>全局参数名</param-name> <param-value>全局参数值</param-value> </context-param> (2)读取全局参数 this.getServletContext().getInitParameter("全局参数名"); //唯一的全局参数从ServletContext(即application对象)中获取