Servlet容器(Tomcat)是如何启动和创建 Spring 容器的?
Servlet容器(Tomcat)是如何启动和创建 Spring 容器的?
一、引言
我们在平时的工作中,几乎不会看到下面的代码:
public static void main(String[] args) {
ApplicationContext apx = new ClassPathXmlApplicationContext("bean-factory.xml");
Car car = (Car) apx.getBean("car");
System.out.println(car);
}
上面的代码,其目的就是手动创建一个 Spring 容器,然后调用容器的 getBean() 方法从容器中获取对应的Bean。
但在现实的项目开发中,我们不会手动创建容器。比如,在Web工程中,我们根本不用管 Spring 容器的启动和创建,只需要在web.xml文件中配置一下,就可以使用 Spring 提供的能力。至于 Tomcat 是如何启动和创建 Spring 的,我们应该了解一下其大致的过程,这样在看源码的时候,不至于一头雾水。
二、Tomcat项目是如何启动的?
第一步:
在启动 Web 项目时,容器(比如 Tomcat )会读取 web.xml 配置文件中的两个节点<context-param>
和<listener>
;那 Tomcat 为什么会读取呢?因为 ServletContextListener
第二步:
接着 Tomcat 会创建一个 ServletContext
(上下文) 对象,该对象的应用范围,是整个 Web 项目都能使用这个上下文;
第三步:
接着 Tomcat 会将读取到<context-param>
转化为键值对,并交给 ServletContext
;
第四步:
接着 Tomcat 会创建 <listener></listener>
中的类实例,即创建监听器。该监听器能够监听 ServletContext
对象的生命周期,实际上就是监听 Web 应用的生命周期。
当Tomcat启动或终止时,会触发ServletContextEvent
事件,该事件由ServletContextListener
来处理。
在ServletContextListener
接口中定义了处理ServletContextEvent
事件的两个方法。
注意:listener定义的类可以是自定义的类但必须需要继承 ServletContextListener
;
第五步:
在监听的类中会有下面两个方法:
初始化方法:在这个方法中可以通过event.getServletContext().getInitParameter("XXXXX")
来得
到context-param
设定的值;
contextInitialized(ServletContextEvent event)
销毁方法: 在这个方法中,多用于关闭应用前释放资源,比如说数据库连接的关闭;
contextDestroyed(ServletContextEvent event)
第六步:
得到这个 context-param 的值之后,你就可以做一些操作了。
注意,这个时候 Web 项目还没有完全启动完成,这个动作会比所有的Servlet都要早!
三、Tomcat启动时如何集成Spring?
1、web.xml配置文件
//配置文件的位置,存放在ServletContext中
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
//监听器类,用于监听ServletContext的生命周期
<listener>
<listener-class>com.scorpios.spring.listener.SpringServletContextListener</listener-class>
</listener>
<servlet>
<description></description>
<display-name>TestServlet</display-name>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.scorpios.spring.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/TestServlet</url-pattern>
</servlet-mapping>
2、自定义的 ServletContextListener
监听器,用于监听ServletContext
的创建和消亡(!!!很重要!!!)
public class SpringServletContextListener implements ServletContextListener {
/*
* 当Servlet容器启动Web应用时调用该方法。在调用完该方法之后,容器再对Filter初始化,
* 并且对那些在Web 应用启动时就需要被初始化的Servlet 进行初始化。
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
// 1. 获取 Spring 配置文件的名称.
ServletContext servletContext = sce.getServletContext();
String config = servletContext.getInitParameter("contextConfigLocation");
// 1. 创建 IOC 容器
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
// 2. 把 IOC 容器放在 ServletContext 的一个属性中.
servletContext.setAttribute("ApplicationContext", ctx);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
}
3、Spring的配置文件applicationContext.xml
<bean id="person" class="com.scorpios.spring.entity.Person">
<property name="username" value="scorpios"></property>
</bean>
4、用于测试的TestServlet
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 从 application 域对象中得到 IOC 容器的引用
ServletContext servletContext = getServletContext();
ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute("ApplicationContext");
// 2. 从 IOC 容器中得到需要的 bean
Person person = ctx.getBean(Person.class);
person.hello();
}
}
5、实体类
public class Person {
private String username;
public void setUsername(String username) {
this.username = username;
}
public void hello() {
System.out.println("My name is " + username);
}
}
6、测试的jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="TestServlet">TestServlet</a>
</body>
</html>
四、测试结果
补充介绍:
1、ServletContext对象
(1)、ServletContext
即 Servlet
上下文对象,该对象表示当前的 Web 应用环境信息,一个Web 应用只会创建一个ServletContext
对象。Web 容器启动的时候,它会为每个Web应用程序都创建一个对应的ServletContext
对象,它代表当前的web应用。而ServletContextListener
就是监听该对象的状态。
(2)、由于一个 Web 应用中的所有Servlet共享一个ServletContext
对象,所以多个Servlet
通过ServletContext
对象实现数据共享,ServletContext
对象通常称为Context域对象。
(3)、我们在说到Servlet
的继承关系时,提到自定义的Servlet,实际上间接实现了Servlet
和ServletConfig
两个接口,其中==ServletConfig
接口中定义了一个方法叫getServletContext
(),用以获取
Servlet`运行的上下文环境对象。==
(4)、每个Web项目,运行时部署在Web应用服务器(如Tomcat、Jetty、WebLogic 等)下,我们称之为一个应用(Application)。我们知道一个Web应用里可以有多个Servlet,而这里的Servlet上下文就可以理解为这些Servlet的运行环境。
2、ServletContext创建时机
(1)、ServletContext
对象是在 Tomcat 服务器加载完当前Web应用后创建出来的(代表当前Web应用);
(2)、ServletContext
对象是作为ServletConfig
对象成员变量传入Servlet
中;
(3)、通过ServletConfig
的getServletContext()
方法就可以得到ServletContext
对象;
(4)、看下ServletConfig
中相关的ServletContext
代码:
//ServletConfig对象中维护了ServletContext对象的应用
class ServletConfig{
ServletContext context;
getInitParameter();
getInitParameterNames();
//返回一个ServletContext对象
public ServletContext getServletContext(){
return contex;
}
}
(5)、在Servet
中的init
的方法实例化一个ServletConfig
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
(6)、this.ServletConfig.getServletContext()
:通过ServletConfig
对象来获取ServletContext
对象。
ServletContext
对象:启动时创建
ServletConfig
对象:调用init
方法之前创建的,在ServletContext
对象之前。
更多推荐
所有评论(0)