一、引言

我们在平时的工作中,几乎不会看到下面的代码:

	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)、ServletContextServlet上下文对象,该对象表示当前的 Web 应用环境信息,一个Web 应用只会创建一个ServletContext对象。Web 容器启动的时候,它会为每个Web应用程序都创建一个对应的ServletContext对象,它代表当前的web应用ServletContextListener就是监听该对象的状态。

(2)、由于一个 Web 应用中的所有Servlet共享一个ServletContext对象,所以多个Servlet通过ServletContext对象实现数据共享,ServletContext对象通常称为Context域对象。

(3)、我们在说到Servlet的继承关系时,提到自定义的Servlet,实际上间接实现了ServletServletConfig两个接口,其中==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)、通过ServletConfiggetServletContext()方法就可以得到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对象之前。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐