1. 程式人生 > >Web工程是如何啟動和建立Spring IOC容器的?

Web工程是如何啟動和建立Spring IOC容器的?

一、引言

我們在平時的工作中,可能很少會看到下面的程式碼:

	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物件之前。