一、引言
我们在平时的工作中,可能很少会看到下面的代码:
public static void main(String[] args) {
ApplicationContext apx = new ClassPathXmlApplicationContext("bean-factory.xml");
Car car = (Car) apx.getBean("car");
System.out.println(car);
}
上面代码会在测试Spring容器的时,在测试类中遇见,其目的就是手动创建一个Spring IOC容器,然后用getBean()方法从容器中对于的类。但在真正的项目中,我们几乎不会手动创建容器,比如,在Web工程中,我们根本不用管Spring IOC容器的启动和创建,只需要在web.xml文件中配置一下,就可以使用Spring的能力了。至于Tomcat是如何启动和创建Spring的,我们应该了解一下其大致的过程,这样在看源码的时候,不至于一头雾水,下面来学习一下。
二、Tomcat项目是如何启动的?
第一步:
在启动Web项目时,容器(比如Tomcat)会读取web.xml配置文件中的两个节点<contex-param>和<listener>;
第二步:
接着Tomcat会创建一个ServletContext(上下文)对象,该对象的应用范围,是整个Web项目都能使用这个上下文;
第三步:
接着Tomcat会将读取到<context-param>转化为键值对,并交给ServletContext;
第四步:
接着Tomcat会创建<listener></listener>中的类实例,即创建监听器。
该监听器能够监听 ServletContext对象的生命周期,实际上就是监听 Web 应用的生命周期。
当Tomcat启动或终止时,会触发ServletContextEvent事件,该事件由ServletContextListener来处理。
在ServletContextListener接口中定义了处理ServletContextEvent事件的两个方法。
注意:listener定义的类可以是自定义的类但必须需要继承ServletContextListener;
第五步:
在监听的类中会有下面两个方法:
初始化方法:
contextInitialized(ServletContextEvent event)
//在这个方法中可以通过event.getServletContext().getInitParameter("XXXXX") 来得
到context-param设定的值;
销毁方法:
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 etc.)下,我们称之为一个应用(Application)。我们知道一个Web应用里可以有多个Servlet,而这里的Servlet上下文就可以理解为这些Servlet的运行环境。
2、ServletContext创建时机
(1)、ServletContext对象是在TomCat服务器加载完当前Web应用后创建出来的(代表当前Web应用);
(2)、ServletContext对象是作为ServletConfig对象成员变量传入Servlet中;
(3)、通过ServletConfig的getServletContext()方法就可以得到ServletContext对象;
(4)、看下ServletConfig中相关的ServletContext代码:
class ServletConfig{ //ServletConfig对象中维护了ServletContext对象的应用
ServletContext context;
getInitParameter();
getInitParameterNames();
public ServletContext getServletContext(){ //返回一个ServletContext对象
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对象之前。