天天看點

Spring架構(二)—— Spring IOC 概念、Spring IOC使用過程一、Spring IOC 概念二、Spring IOC 使用過程

文章目錄

  • 一、Spring IOC 概念
    • 1、Spring IOC 概述
    • 2、自定義 IOC 架構核心步驟
  • 二、Spring IOC 使用過程
    • 1、Spring IOC 使用步驟
    • 2、Spring IOC 使用過程
    • 3、案例

一、Spring IOC 概念

1、Spring IOC 概述

(1)IOC(Inverse Of Control)是Spring核心功能之一,“控制反轉”(IOC) 将應用程式的配置和依賴性規範與實際的應用程式代碼分開。

舉個例子:在開發過程中業務層需要調用資料層的方法,這時我們會選擇在業務層中手動建立資料層對象。Spring的IOC緻力于将對象的建立過程以及對象的依賴性關系通過應用程式來完成,控制權交還給了應用程式,是以被稱為控制反轉。使用Spring我們會将資料層對象和業務層對象的建立工作交給Spring來負責,由于業務層中需要調用資料層方法,也就是說業務層對象對于資料層對象有依賴,那麼Spring在建立業務層對象的同時,還會将資料層對象注入到業務層對象中,該過程被稱為依賴注入(DI)。

(2)DI(Dependency Injection),即依賴注入:元件之間依賴關系由容器在運作期決定,形象的說,即由容器動态的将某個依賴關系注入到元件之中。依賴注入的目的并非為軟體系統帶來更多功能,而是為了提升元件重用的頻率,并為系統搭建一個靈活、可擴充的平台。

2、自定義 IOC 架構核心步驟

(1)通過xml檔案定義需要交給容器來建立的java類型,以及對象之間的依賴關系。

(2)通過xml解析讀取xml資訊,使用java反射來建立所需的java對象。

(3)将建立好的java對象儲存到全局的對象容器中(在spring中被稱為spring容器)

(4)通過xml的配置将A對象中所依賴的其他類型的B對象注入到A對象中

(5)在需要使用對象時,從容器中取出對象使用。

二、Spring IOC 使用過程

1、Spring IOC 使用步驟

(1)項目中導入spring所需jar包

Spring架構(二)—— Spring IOC 概念、Spring IOC使用過程一、Spring IOC 概念二、Spring IOC 使用過程

(2)在src目錄下建立applicationContext.xml(如果是web項目則需要在WEB-INF目錄下建立)。

(3)在xml頂部導入schema,schema和dtd不同,dtd是一個獨立的标簽,而schema是屬于根标簽的屬性。是以在導入schema時,也就定義好了根标簽。以下是一份完整的schema包含了spring-framework中用到的所有内容,除了springmvc。因為springmvc的配置和spring的配置檔案是分開的。

<beans	
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" 	xmlns:p="http://www.springframework.org/schema/p" 	xmlns:util="http://www.springframework.org/schema/util" 	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/jdbc
		http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
		http://www.springframework.org/schema/cache
		http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-4.0.xsd
		http://www.springframework.org/schema/util
		http://www.springframework.org/schema/util/spring-util-4.0.xsd">
</beans>
           

(4)在配置檔案中使用bean标簽來定義spring容器負責建立的類,以及類的依賴關系,最終spring在建立好對象以後會自動完成依賴注入。

① service 層

public class UserService {
	private UserDao userDao;
	public UserDao getUserDao() {
		return userDao;
	}
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public void insert() {
		System.out.println("執行新增業務");
		userDao.insert();
	}
}
           

② dao層

public class UserDao {
	public void insert() {
		System.out.println("執行新增資料");
	}
}
           

③ xml檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="service" class="com.spring.demo1.service.UserService">
        <property name="userDao" ref="dao"></property>
    </bean>
    <bean id="dao" class="com.spring.demo1.dao.UserDao"></bean>
</beans>
           

(5)解析xml檔案

public class UserBean {
	//定義容器儲存架構建立的所有對象
	private static Map<String,Object> container = new HashMap<String,Object>();
	
	static {
		//使用dom4j解析xml檔案讀取bean标簽,将對象建立出來用id為鍵,對象為值儲存到容器中
		SAXReader reader = new SAXReader();
		try {
			//讀取xml檔案
			Document document = reader.read("src/spring1.xml");
			//擷取根便簽
			Element rootElement = document.getRootElement();
			//讀取bean标簽,得到對應對象集合
			List<Element> beans = rootElement.elements("bean");
			
			//擷取bean标簽中的id作為鍵,class對象值,并儲存到容器中
			for(Element b: beans) {
				//擷取id鍵
				String key = b.attributeValue("id");
				//擷取class值
				String value = b.attributeValue("class");
				//存入容器中
				container.put(key, Class.forName(value).newInstance());
			}
			
			//根據property标簽定義的依賴關系完成依賴注入
			for(Element b: beans) {
				//擷取bean下的property标簽
				List<Element> properties = b.elements("property");
				for(Element p: properties) {
					//需要被指派的屬性名稱
					String pName = p.attributeValue("name");
					//該屬性需要指派的對象在容器裡的id
					String pRef = p.attributeValue("ref");
					//從容器中拿到被依賴的對象
					Object refObject = container.get(pRef);
					
					//根據屬性名稱擷取set方法
					//根據bean标簽拿到正在循環的這個bean标簽的id
					String key = b.attributeValue("id");
					//從容器中取出依賴方對象
					Object object = container.get(key);
					//擷取依賴方的位元組碼資訊
					Class c = object.getClass();
					//擷取需要被指派的屬性對象
					Field field = c.getDeclaredField(pName);
					//擷取該屬性的set方法
					Method method=c.getDeclaredMethod("set"+pName.substring(0, 1).toUpperCase()+pName.substring(1), field.getType());
					//調用方法将被依賴對象指派給依賴對象的目标屬性
					method.invoke(object, refObject);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public Object getBean(String id) {
		return container.get(id);
	}
}
           

(6)建立測試類,在入口方法中從spring容器裡面取出業務層對象,執行方法。

public class UserAction{
	public static void main(String[] args) {
		UserBean factory = new UserBean();
		UserService service = (UserService) factory.getBean("service");
		service.insert();
	}
}
           

(7)另外也可以不用dom4j來解析xml檔案,而是直接用下面的方法來擷取xml檔案内容。

public class UserAction{
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("hk1.xml");
		UserService service = (UserService) ac.getBean("service");
		service.insert();
	}
}
           

這樣就可以省略步驟(5)和步驟(6),而用步驟(7)代替。

2、Spring IOC 使用過程

(1)工廠模式

假如我們的某個對象不能用new的方式來擷取,而是我們或者是其他第三方工具使用工廠模式來産生的,那麼我們在定義bean标簽時需要,需要把

<bean>

的class屬性配置為工廠類(或者把

factory-bean

屬性配置為工廠類對象),使用屬性

factoryMethod

來定義工廠類中擷取此對象的方法。

或者

<bean id="dbutil" class="com.spring.util.DBUtil"></bean>
<bean id="connection" factory-bean="dbutil" factory-method="openConnection"></bean>
           

(2)選擇構造函數建立對象

在建立對象時,如果想通過不同的構造方法建立,可以在bean标簽下使用

<constructor-arg>

标簽來定義構造函數的參數,一個

<constructor-arg>

标簽就表示一個參數,如果構造函數有多個标簽則需要使用多個

<constructor-arg>

标簽,前提是類中必須定義對應的帶參構造函數。

① 業務實作層

private UserServiceImp(){}
private UserServiceImp(String name){
	System.out.println(name);
}
private UserServiceImp(String name, int age){
	System.out.println(name);
	System.out.println(age);
}
           

② 配置檔案

<bean id="userService" class="com.spring.service.UserServiceImp">
	<constructor-arg>
		<value>張三</value>
	</constructor-arg>
	<constructor-arg>
		<value>18</value>
	</constructor-arg>
	<property name="dao" ref="userDao"></property>
</bean>
           

(3)單例模式

Spring 在建立對象時,預設采用的是單例模式,也就是在 Spring 容器中,同一個類型的對象隻會儲存一個,如果想用非單例模式則需要在 bean 标簽中使用

singleton="false"

(spring4以前)或者

scope="prototype"

(spring4.0以後)來進行修改。

scope 在 web 環境中還可以設定為 request 和 session

  • request:表示每次發送新的請求都會建立一個對象
  • session:表示在一次會話中使用的是同一個對象
  • <property>

    标簽配置:

    <property>

    标簽用于向對象中進行屬性注入,注入的屬性可以是基本資料類型、String、集合、鍵值對、對象。

    <property>

    标簽需要配置在對應的

    <bean>

    标簽之下,

    <property>

    标簽中的 name 屬性表示的對象中的屬性名稱。

① 基本資料類型和字元串的注入

基本資料類型和字元串的注入都可以使用

<property>

标簽下的

<value>

子标簽完成注入。

如:

<property name="password">
	<value>值</value>
</property>
           

如果要給字元串屬性注入空串

<value>

标簽中空着就行了,但是如果想要注入的是null,則需要使用

<null/>

子标簽。

<property name="password">
	<null></null>
</property>
           

② 對象注入

注入對象的方式有兩種:

第一種注入已有的 bean 對象,使用 ref 屬性

ref="bean id"

的方式或者使用

<ref bean="bean id">

子标簽都可以。

或者

<property name="dao">
	<ref="userDao" />
</property>
           

第二種注入還未通過

<bean>

标簽建立的對象,在

<property>

标簽中使用

<bean>

标簽來定義匿名對象。

<property name="dao">
	<bean class="com.spring.dao.UserDaoImp"></bean>
</property>
           

③ List 注入

要為對象注入List集合需要在

<property>

中使用

<list>

子标簽,

<list>

标簽中可通過

<value>

标簽配置任意類型對象。如果為Java對象,則使用 ref指定,或者使用

<bean>

定義新執行個體,如果是普通類型如 String、int、double 等,直接用字元串即可。

<list/>

裡的元素會按配置的先後順序排序。

<property name="names">
	<list>
		<value>值1</value>
		<value>值2</value>
		<ref bean="bean id" />
	</list>
</property>
           

④ Set 注入

要為對象注入Set集合需要在

<property>

中使用

<set>

子标簽,

<set>

标簽中可通過

<value>

标簽配置任意類型對象。如果為 Java 對象,則使用ref指定,或者使用

<bean>

定義新執行個體,如果是普通類型如 String、int、double 等,直接用字元串即可。

<property name="names">
	<set>
		<value>值1</value>
		<value>值2</value>
		<ref bean="bean id" />
	</set>
</property>
           

⑤ Map 注入

要為對象注入 Map 鍵值對需要使用

<property>

的子标簽

<map>

<entry>

配置 Map 裡的元素,key 指定鍵,如果 key 為對象,使用 key-ref 屬性,value 指定值。如果為 Java 對象,則使用 ref 指定,或者使用

<bean>

定義新執行個體。

<property name="names">
	<map>
		<entry key="鍵1">
			<value>值1</value>
		</entry>
		<entry key="鍵2">
			<ref bean="bean id" />
		</entry>
	</map>
</property>
           

(4)在

applicationContext.xml

檔案中使用外界資源檔案

有時我們在配置

<bean>

标簽時,需要注入一下資料給對象,例如,當我們使用 Spring 來建立 JDBC Connection 對象或者是連接配接池對象時,都需要配置連接配接資料庫的相關資訊,我們可以直接在配置檔案中寫,但是這樣不利于維護,我們可以将這些資訊配置在資源檔案中,然後通過加載資源檔案,在

applicationContext.xml

檔案中直接使用資源檔案的值。

例如:使用 Spring 建立 c3p0 連接配接池對象

① 首先導入 c3p0 的jar包以及它的依賴包

在src下配置一個

c3p0.properties

的資源檔案,在該資源檔案中定義 jdbc 的連接配接資訊以及 c3p0 的各種資訊。

#jdbc連接配接資訊
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl = jdbc:mysql://localhost:3306/ebuy?useUnicode=true&characterEncoding=utf-8
jdbc.user = root
jdbc.password =
#c3p0各項屬性
c3p0.minPoolSize = 10
c3p0.maxPoolSize = 50
c3p0.initialPoolSize = 20
c3p0.maxIdleTime = 60
c3p0.acquireIncrement = 1
c3p0.checkoutTimeout=6000
c3p0.idleConnectionTestPeriod=600
           

② 在

applicationContext.xml

中使用

<bean>

标簽建立資源檔案加載器對象,該對象不需要定義 id,Spring 會通過類型完成注入。

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location" value="classpath:c3p0.properties"/>
<bean>
           

④ 使用

<bean>

标簽建立 c3p0 資料源對象,使用 el 表達式擷取資源檔案中的資料,将該資料注入到c3p0的各項屬性中去。

<bean id="pool" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<!--提供資料庫連接配接資訊和連接配接池的常用資訊-->
    	<!--驅動類-->
    	<property name="driverClass" value="${jdbc.driverClass}" />
    	<!--連接配接資料庫的位址-->
	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
	<!--連接配接資料庫的使用者名-->
	<property name="user" value="${jdbc.user}" />
	<!--連接配接資料庫的密碼-->
	<property name="password" value="${jdbc.password}" />
	
	<!--最大連接配接數-->
	<property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
	<!--最小連接配接數-->
	<property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
	<!--初始化連接配接數-->
	<property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
	<!--自增長連接配接數-->
	<property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
	<!--連接配接對象空閑時間  超出10分鐘銷毀-->
	<property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
	<!--連接配接資料庫的等待時間 超出等待時間 抛出異常-->
	<property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"></property>
	<!--檢查連接配接的間隔時間-->
	<property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"></property>
</bean>
           

⑤ 在需要該連接配接池對象的類中,定義對應的屬性,提供 get、set 方法,再通過

<bean>

标簽完成依賴注入即可。

需要使用連接配接池對象的類:

public class UserDaoImp implements UserDao {
	private DataSource dataSource;
	public DataSource getDataSource() {
		return dataSource;
	}
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
}
           

<bean>

标簽完成依賴注入:

<bean id="userDao " class="com.spring.dao.imp.UserDaoImp">
	<property name="dataSource" ref="pool"></property>
</bean>
           

3、案例

① service層

public class UserService {
	private UserDao userDao;
	public UserDao getUserDao() {
		return userDao;
	}
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public void select() {
		System.out.println("正在執行查詢業務");
		userDao.select();
	}
}
           

② dao層

public class UserDao {
	private DataSource dataSource;
	public DataSource getDataSource() {
		return dataSource;
	}
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	public void select() {
		System.out.println("執行新增資料");
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			conn = dataSource.getConnection();
			String sql = "select * from products";
			ps = conn.prepareStatement(sql);
			ResultSet resultSet = ps.executeQuery();
			while(resultSet.next()) {
				System.out.println(resultSet.getString("p_name"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}
           

③ applicationContext.xml檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans  
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"     xmlns:p="http://www.springframework.org/schema/p"   xmlns:util="http://www.springframework.org/schema/util"     xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/jdbc
		http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
		http://www.springframework.org/schema/cache
		http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-4.0.xsd
		http://www.springframework.org/schema/util
		http://www.springframework.org/schema/util/spring-util-4.0.xsd">
		
    <bean id="service" class="com.spring.hk1.service.UserService">
        <property name="userDao" ref="dao"></property>
    </bean>
    <bean id="dao" class="com.spring.hk1.dao.UserDao">
        <property name="dataSource" ref="pool"></property>
    </bean>
	
	<!-- 建立連接配接池 -->
	<bean id="pool" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--提供資料庫連接配接資訊和連接配接池的常用資訊-->
        <!--驅動類-->
        <property name="driverClass" value="${jdbc.driverClass}" />
        <!--連接配接資料庫的位址-->
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
        <!--連接配接資料庫的使用者名-->
        <property name="user" value="${jdbc.user}" />
        <!--連接配接資料庫的密碼-->
        <property name="password" value="${jdbc.password}" />
        
        <!--最大連接配接數-->
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
        <!--最小連接配接數-->
        <property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
        <!--初始化連接配接數-->
        <property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
        <!--自增長連接配接數-->
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
        <!--連接配接對象空閑時間  超出10分鐘銷毀-->
        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
        <!--連接配接資料庫的等待時間 超出等待時間 抛出異常-->
        <property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"></property>
        <!--檢查連接配接的間隔時間-->
        <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"></property>
    </bean>
    <!-- 建立資源檔案加載器對象 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
       <property name="location" value="classpath:c3p0.properties"></property>
    </bean>
</beans>
           

④ 測試類:Action類

public class UserAction{
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring2.xml");
		UserService service = (UserService) ac.getBean("userService");
		service.select();
	}
}