Java Servlet 2
Tomcat中的类装载器
Bootstrap |
System |
Webapp2 |
Webapp1 |
Shared |
Catalina |
Common |
Bootstrap为Java虚拟机内嵌的类装载器与ExtClassLoader的总称,负责加载Java核心包中的类和存放在<JAVA_HOME>/jre/lib/ext目录下的类。
System即系统类装载器,通常情况下就是AppClassLoader,负责加载CLASSPATH环境变量设置的目录中的类,Tomcat不会继承操作系统原来设置好的CLASSPATH环境变量的内容,而是将CLASSPATH环境变量重新设置为仅包含如下两个jar包:
<CATALINA_HOME>/bin/bootstrap.jar
<JAVA_HOME>/lib/tools.jar
Common类装载器负责从<CATALINA_HOME>/common/classes中的.class类文件和<CATALINA_HOME>/common/lib的jar包加载类。
Catalina类装载器负责从<CATALINA_HOME>/server/classes中的.class类文件和<CATALINA_HOME>/server/lib中的jar包加载类。
Shared类装载器负责从<CATALINA_HOME>/share/classes中的.class类文件和<CATALINA_HOME>/share/lib中的jar包加载类。
WebappX类装载器负责从当前Web应用程序的/WEB-INF/classes中的.class类文件和/WEB-INF/lib中的jar包加载类。
注意:区分Servlet的编译和运行环境。
Servlet的特点:
Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,她不能独立运行,它的运行完全由Servlet引擎来控制和调度。
Servlet引擎是一种容器程序,它负责管理和维护所有Servlet对象的声明周期。Servlet的加载,执行流程,以及如何接收客户端发送的数据和如何将数据传输到客户端等具体的底层事务,都是由Servlet引擎来实现的,Servlet引擎负责将客户端的请求信息转交给Servlet和将Servlet生成的响应信息返回给客户端。
Servlet属于一种插件,它是一个提供了一些约定方法供容器去调用的类,它只负责在自身的方法中接收并处理容器传递进来的数据,以及生成并返回容器去使用的数据和状态信息。
Servlet的最常见的应用在于读取WEB浏览器传递给WEB服务器的参数和生成WEB服务器返回给WEB浏览器的动态网页内容文档内容,Servlet也能获取WEB浏览器发送的HTTP请求消息中的各个请求头和请求行信息,以及生成用于WEB服务器发送的HTTP响应消息中的状态行和响应头信息;
Servlet还能获取WEB服务器和Servlet引擎自身的一些环境和状态信息。
Servlet的运行过程:
① Servlet引擎检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
② 装载并创建该Servlet的一个实例对象。
③ 调用Servlet实例对象的init()方法。
④ 创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
⑤ WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
在<servlet>元素中嵌入一个<load-on-startup>子元素,WEB应用程序在启动时就可以装载并创建Servlet的实例对象,以及调用Servlet实例对象的init()方法。
<load-on-startup>中的数字越小,则装载越早,如果是负整数则由servlet引擎决定先创建那个,如果两个数字相同则有servlet引擎决定先加载那个。
在Servlet的整个声明周期内,它的init方法只被调用一次,而对一个Servlet的每一次访问请求都导致Servlet引擎调用一次servlet的service方法,对于每次访问的请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给service()方法。
Tomcat也提供了是否自动重新装载被修改的Servlet的配置选项,在<Tomcat安装目录>/conf/server.xml文件中,可以将<Context>元素的reloadable属性设置为true.这样,Tomcat将监视该WEB应用程序的/WEB-IND/classes和/WEB-IND/lib目录下的类是否发生了改变,然后自动重新装载那些发生了改变的类。
也可以通过tomcat的管理程序重新加载修改过的Servlet程序
注意:虽然Servlet源程序中引用的是Servlet API,但Servlet运行时真正调用的对象是由Servlet容器中的实现类创建的,所以,将Servlet API的jar包增加到CLASSPATH环境变量中,只能保证Servlet程序可以被成功编译,但不能让Servlet程序离开Servlet容器运行。
Servlet的线程安全问题:
Servlet引擎采用多线程模式运行,它为并发的每个访问请求都使用一个独立的线程来进行响应,但带来了线程安全的问题。
如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用service方法。
SingleThreadModel接口中没有定义任何方法,只要在Servlet类的定义中增加实现SingleThreadModel接口声明即可。
对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Server实例对象。
实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,事实上,在Servlet API2.4中,已经将SingleThreaModel标记为Deprecated(过时的)。
在Servlet定义了成员变量,和访问数据库,或者Servlet容器时,可能出现线程安全问题。
ServletConfig接口
Servlet在有些情况下可能需要访问Servlet容器或者借助Servlet容器访问外部的资源,所以Servlet引擎需要将表示Servlet容器的对象传递给Servlet。另外,在web.xml文件中为某个Servlet设置的友好名称和初始化参数等信息也需要传递给该Servlet.
<servlet>
<servlet-name>ConfigTest</servlet-name>
<servlet-class>ConfigTestServlet</servlet-class>
<init-param>
<param-name>Corporation</param-name>
<param-value>北京传智播客有限公司</param-value>
</init-param>
</servlet>
Servlet引擎将代表Servlet容器的对象和Servlet的配置参数信息一并封装到一个称为ServletConfig的对象中,并在初始化Servlet实例对象时一并封装到一个称为ServletConfig的对象中,并在初始化Servlet实例对象时传递给该Servlet。ServletConfig接口则用于与定义ServletConfig对象需要对外提供的方法,以便在Servlet程序中可以调用这些方法来获取有关信息。
Servlet引擎调用Servlet的实例对象的init(ServletConfig config)方法将ServletConfig对象传递给Servlet.Servlet.getServletConfig()方法必须返回init(ServletConfig config)方法传递进来的这个ServletConfig对象的引用。
ServletConfig接口的方法:
getInitParameterNames
getInitParameter
getServletName
Servlet接口中定义了一个getServletConfig方法,该方法必须返回Servlet容器调用Servlet.init(ServletConfig config)方法时传递进来的那个ServletConfig对象的引用。GenericServlet类已经按此要求实现了getServletConfig方法。
在Servlet程序中如何调用ServletConfig对象的方法
举例: String servletName = getServletConfig().getServletName();
GenericServlet类是如何实现ServletConfig接口中方法
举例:
public String getServletName() {
return getServletConfig().getServletName();
}
在Servlet程序中调用ServletConfig对象的方法的简单方式:
举例:
String servletName = getServletName();