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