Spring4(一)IOC和DI

1、Spring簡介

Spring 是一個開源框架.
Spring 爲簡化企業級應用開發而生使用 Spring 可以使簡單的 JavaBean 實現以前只有 EJB 才能實現的功能

        •Spring 是一個 IOC(DI) AOP 容器框架

具體描述 Spring:
輕量級Spring 是非侵入性- 基於 Spring 開發的應用中的對象可以不依賴於 Spring API
依賴注入(DI --- dependency injectionIOC)
面向切面編程(AOP --- aspect oriented programming)
容器:Spring 是一個容器因爲它包含並且管理應用對象的生命週期
框架:Spring 實現了使用簡單的組件配置組合成一個複雜的應用Spring 中可以使用 XML Java 註解組合這些對象;
一站:在 IOC AOP 的基礎上可以整合各種企業應用的開源框架和優秀的第三方類庫 (實際上 Spring 自身也提供了展現層的 SpringMVC 和 持久層的 Spring JDBC

 

Spring的模塊:

 

2、Eclipse安裝Spring插件

Eclipse工具想要更好更快的開發Spring,需要安裝一個插件:SPRING TOOL SUITE 利用該插件可以更方便的在 Eclipse 平臺上開發基於 Spring 的應用。

安裝方法說明springsource-tool-suite-3.4.0.RELEASE-e4.3.1-updatesite.zip
Help --> Install New Software...
Click Add...
In dialog Add Site dialog, click Archive...
Navigate to springsource-tool-suite-3.4.0.RELEASE-e4.3.1-updatesite.zip  and click  Open
Clicking OK in the Add Site dialog will bring you back to the dialog 'Install'
Select the xxx/Spring IDE that has appeared
Click Next  and then Finish
Approve the license
Restart eclipse when that is asked

本次我使用的是InteliJ IDEA2019版作爲開發工具,所以跳過了上述插件安裝。

 

3、入門程序HelloWorld

3.1 環境準備

1、新建一個普通的Java工程:

File——》new ——》Project——》Java——》JavaEE,然後一路next知道完成。

2、引入依賴的jar包

本次需要用到的jar包:spring-core-4.0.0.RELEASE.jar,spring-beans-4.0.0.RELEASE.jar,spring-context-4.0.0.RELEASE.jar,spring-expression-4.0.0.RELEASE.jar和commons-logging-1.1.1.jar。

我們首先要把這幾個包放到同一個目錄下面,然後在IDEA中操作:

File——》Project Structure...——》Libraries——》點擊+號,選擇Java——》然後選擇我們剛纔存放jar包的目錄即可。

我們就可以在工程的 External Libraries下面看到我們剛纔引的jar包的目錄,裏面有我們需要的jar包了。


3、創建資源文件夾
在工程的根目錄下面,創建一個conf的資源文件夾,用來存放一些Spring的配置文件,以及其他配置文件(比如日誌文件)。

右鍵工程名——》new——》Directory——》文件名稱爲:conf

右鍵conf目錄——》Mark Directory as——》Resources Root,那麼這個conf就變成了資源目錄了。

在conf目錄下面創建一個spring目錄,用來存放Spring的主配置文件:applicationContext.xml
 

3.2 代碼實現

3.2.1 創建HelloWorld

在src下面創建包:com.spring.beans,然後創建HelloWorld:

package com.spring.beans;

public class HelloWorld {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void sayHello(){
        System.out.println("Hello  "+name);
    }
}

3.2.2 配置文件applicationContext.xml

在conf/spring下面創建applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <!--告訴Spring去創建一個變量爲helloWorld的對象-->
    <bean id="helloWorld" class="com.spring.beans.HelloWorld">
        <!--調用HelloWorld的setName方法給name屬性賦值-->
        <!--這兩個操作都在Spring初始化的時候進行-->
        <property name="name" value="Spring"></property>
    </bean>

</beans>

3.2.3 測試

package com.spring.test;

import com.spring.beans.HelloWorld;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public static void main(String[] args) {
        //通過配置文件創建Spring的IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        //從IOC容器中,得到helloWorld對象
        HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld");
        helloWorld.sayHello();//調用方法
    }
}

打印結果:Hello  Spring

通過入門程序,我們發現,之前我們需要創建對象,給對象的屬性賦值,現在的這些操作都是Spring幫我們做了,在通過配置文件創建Spring容器的時候,就會初始化配置文件裏面所有的實例對象,並且賦值。這就是SPring的IOC和DI。

 

4、Spring中Bean的配置

4.1 IOC和DI概述

IOC(Inversion of Control):其思想是反轉資源獲取的方向傳統的資源查找方式要求組件向容器發起請求查找資源作爲迴應容器適時的返回資源而應用了 IOC 之後則是容器主動地將資源推送給它所管理的組件組件所要做的僅是選擇一種合適的方式來接受資源這種行爲也被稱爲查找的被動形式
 
DI(Dependency Injection) IOC 的另一種表述方式:即組件以一些預先定義好的方式(例如: setter 方法)接受來自如容器的資源注入相對於 IOC 而言,這種表述更直接

 

以前我們要創建一個對象,都是通過new 來獲取,而現在,只要在配置文件配置了類全名,Spring就會通過發射幫我們把這個對象創建出來,存儲到IOC容器中,到時候我們直接從容器裏面取,其實這就是IOC;

同樣,以前的時候,對象裏面的屬性賦值,需要提高set方法,我們手動的調用set方法給對象屬性賦值,但是現在IOC容器可以把我們需要的值按照配置信息,自動注入到我們的對象中。這就是DI。

4.2 Bean配置概述

配置 bean
配置形式:基於 XML 文件的方式;基於註解的方式
Bean 的配置方式:通過全類名(反射)、通過工廠方法(靜態工廠方法 & 實例工廠方法)、FactoryBean
IOC 容器 BeanFactory & ApplicationContext 概述
依賴注入的方式:屬性注入;構造器注入
注入屬性值細節
自動裝配
bean 之間的關係:繼承;依賴
bean 的作用域:singleton(單例)prototypeWEB 環境作用域
使用外部屬性文件
spEL
IOC 容器中 Bean 的生命週期
Spring 4.x 新特性:泛型依賴注入

 

4.3 Spring容器

Spring IOC 容器讀取 Bean 配置創建 Bean 實例之前, 必須對它進行實例化,只有在容器實例化後, 纔可以從 IOC 容器裏獲取 Bean 實例並使用。
Spring 提供了兩種類型的 IOC 容器實現:
BeanFactory:IOC 容器的基本實現;
ApplicationContext提供了更多的高級特性BeanFactory 的子接口;
BeanFactory Spring 框架的基礎設施,面向 Spring 本身;ApplicationContext 面向使用 Spring 框架的開發者,幾乎所有的應用場合都直接使用 ApplicationContext 而非底層的 BeanFactory。
無論使用何種方式,,配置文件時相同的。

4.3.1 ApplicationContext

ApplicationContext 的主要實現類:
ClassPathXmlApplicationContext類路徑下加載配置文件
FileSystemXmlApplicationContext從文件系統中加載配置文件
ConfigurableApplicationContext 擴展於 ApplicationContext,新增加兩個主要方法:refresh() close(), 讓 ApplicationContext 具有啓動、刷新和關閉上下文的能力
ApplicationContext 在初始化上下文時就實例化所有單例的 Bean
WebApplicationContext 是專門爲 WEB 應用而準備的,它允許從相對於 WEB 根目錄的路徑中完成初始化工作

 

4.4 依賴注入

Spring支持3中注入方式:

      屬性注入(settre方法);

      構造器注入;

      工廠方法注入(很少用,不推薦)。

4.4.1 屬性注入

屬性注入即通過 setter 方法注入Bean 的屬性值或依賴的對象
屬性注入使用 <property> 元素使用 name 屬性指定 Bean 的屬性名稱,value 屬性或 <value> 子節點指定屬性值
屬性注入是實際應用中最常用的注入方式
<!--告訴Spring去創建一個變量爲helloWorld的對象-->
    <bean id="helloWorld" class="com.spring.beans.HelloWorld">
        <!--調用HelloWorld的setName方法給name屬性賦值-->
        <!--這兩個操作都在Spring初始化的時候進行-->
        <property name="name" value="Spring"></property>
    </bean>

4.4.2 構造器注入

通過構造方法注入Bean 的屬性值或依賴的對象,它保證了 Bean 實例在實例化後就可以使用。
構造器注入在 <constructor-arg> 元素裏聲明屬性, <constructor-arg> 中沒有 name 屬性

 

按索引匹配入參:

按類型匹配入參:

4.5 不同類型的值的注入 

4.5.1 字面值

字面值:可用字符串表示的值,可以通過 <value> 元素標籤或 value 屬性進行注入。
基本數據類型及其封裝類、String 等類型都可以採取字面值注入的方式。
若字面值中包含特殊字符,可以使用 <![CDATA[]]> 把字面值包裹起來。

4.5.2 引入其他Bean

組成應用程序的 Bean 經常需要相互協作以完成應用程序的功能. 使 Bean 能夠相互訪問, 就必須在 Bean 配置文件中指定對 Bean 的引用
Bean 的配置文件中, 可以通過 <ref> 元素或 ref  屬性Bean 的屬性或構造器參數指定對 Bean 的引用

也可以在屬性或構造器裏包含 Bean 的聲明, 這樣的 Bean 稱爲內部 Bean
<!-- 聲明使用內部 bean -->
	<bean id="service2" class="com.atguigu.spring.ref.Service">
		<property name="dao">
			<!-- 內部 bean, 類似於匿名內部類對象. 不能被外部的 bean 來引用, 也沒有必要設置 id 屬性 -->
			<bean class="com.atguigu.spring.ref.Dao">
				<property name="dataSource" value="c3p0"></property>
			</bean>
		</property>
	</bean>

4.5.3 集合屬性注入 

Spring可以通過一組內置的 xml 標籤(例如: <list>, <set> <map>) 來配置集合屬性.
配置 java.util.List 類型的屬性需要指定 <list>  標籤在標籤裏包含一些元素這些標籤可以通過 <value> 指定簡單的常量值通過 <ref> 指定對其他 Bean 的引用通過<bean> 指定內置 Bean 定義通過 <null/> 指定空元素甚至可以內嵌其他集合
數組的定義和 List 一樣, 都使用 <list>
配置 java.util.Set 需要使用 <set> 標籤定義元素的方法與 List 一樣
Java.util.Map 通過 <map> 標籤定義, <map> 標籤裏可以使用多個 <entry> 作爲子標籤每個條目包含一個鍵和一個值.
必須在 <key> 標籤裏定義鍵
因爲鍵和值的類型沒有限制所以可以自由地爲它們指定 <value>, <ref>, <bean> <null> 元素.
可以將 Map 的鍵和值作爲 <entry> 的屬性定義: 簡單常量使用 key value 來定義; Bean 引用通過 key-ref value-ref 屬性定義
使用 <props> 定義 java.util.Properties該標籤使用多個 <prop> 作爲子標籤每個 <prop> 標籤必須定義 key 屬性
使用基本的集合標籤定義集合時, 不能將集合作爲獨立的 Bean 定義, 導致其他 Bean 無法引用該集合, 所以無法在不同 Bean 之間共享集合,可以使用 util schema 裏的集合標籤定義獨立的集合 Bean. 需要注意的是, 必須在 <beans> 根元素裏添加 util schema 定義
<!-- 聲明集合類型的 bean -->
	<util:list id="cars">
		<ref bean="car"/>
		<ref bean="car2"/>
	</util:list>
	
	<bean id="user2" class="com.atguigu.spring.helloworld.User">
		<property name="userName" value="Rose"></property>
		<!-- 引用外部聲明的 list -->
		<property name="cars" ref="cars"></property>
	</bean>

4.6 XML 配置裏的 Bean 自動裝配

Spring IOC 容器可以自動裝配 Bean。需要做的僅僅是<bean> autowire 屬性裏指定自動裝配的模式
byType(根據類型自動裝配):IOC 容器中有多個與目標 Bean 類型一致的 Bean, 在這種情況下,Spring 將無法判定哪個 Bean 最合適該屬性所以不能執行自動裝配
byName(根據名稱自動裝配):必須將目標 Bean 的名稱和屬性名設置的完全相同
constructor(通過構造器自動裝配):Bean 中存在多個構造器時, 此種自動裝配方式將會很複雜. 不推薦使用
 

缺點:

Bean 配置文件裏設置 autowire 屬性進行自動裝配將會裝配 Bean 的所有屬性然而若只希望裝配個別屬性時autowire 屬性就不夠靈活了
autowire 屬性要麼根據類型自動裝配要麼根據名稱自動裝配不能兩者兼而有之
一般情況下,在實際的項目中很少使用自動裝配功能,因爲和自動裝配功能所帶來的好處比起來,明確清晰的配置文檔更有說服力一些

 

4.7 Spring Bean的作用域

Spring , 可以在 <bean> 元素的 scope 屬性裏設置 Bean 的作用域
默認情況下, Spring 只爲每個在 IOC 容器裏聲明的 Bean 創建唯一一個實例, 整個 IOC 容器範圍內都能共享該實例所有後續的 getBean() 調用和 Bean 引用都將返回這個唯一的 Bean 實例該作用域被稱爲 singleton它是所有 Bean 的默認作用域

prototype:原型的, 每次調用 getBean 方法都會返回一個新的 bean. 且在第一次調用 getBean 方法時才創建實例
singleton:單例的, 每次調用 getBean 方法都會返回同一個 bean. 且在 IOC 容器初始化時即創建 bean 的實例. 默認值

4.8 使用外部配置文件

配置文件裏配置 Bean , 有時需要在 Bean 的配置裏混入系統部署的細節信息(例如: 文件路徑, 數據源配置信息等). 而這些部署細節實際上需要和 Bean 配置相分離
Spring 提供了一個 PropertyPlaceholderConfigurer BeanFactory 後置處理器這個處理器允許用戶將 Bean 配置的部分內容外移到屬性文件可以在 Bean 配置文件裏使用形式爲 ${var} 的變量PropertyPlaceholderConfigurer 從屬性文件里加載屬性並使用這些屬性來替換變量.
Spring 還允許在屬性文件中使用 ${propName},以實現屬性之間的相互引用。

需要到context命名空間,在配置文件中引入如下配置:

<!-- 導入外部的資源文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	
	<!-- 配置數據源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		
		<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
		<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
	</bean>

外部文件配置:db.properties

jdbc.user=root
jdbc.password=1230
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///test

jdbc.initPoolSize=5
jdbc.maxPoolSize=10

 

4.9 Spring表達式語言:SPEL

Spring 表達式語言(簡稱SpEL):是一個支持運行時查詢和操作對象圖的強大的表達式語言
語法類似於 ELSpEL 使用 #{…} 作爲定界符,所有在大框號中的字符都將被認爲是 SpEL
SpEL bean 的屬性進行動態賦值提供了便利
通過 SpEL 可以實現:
通過 bean id bean 進行引用
調用方法以及引用對象中的屬性
計算表達式的值
正則表達式匹配
<!-- 測試 SpEL: 可以爲屬性進行動態的賦值(瞭解) -->
	<bean id="girl" class="com.atguigu.spring.helloworld.User">
		<property name="userName" value="周迅"></property>
	</bean>
	
	<bean id="boy" class="com.atguigu.spring.helloworld.User" init-method="init" destroy-method="destroy">
		<property name="userName" value="高勝遠"></property>
		<property name="wifeName" value="#{girl.userName}"></property>
	</bean>

 

4.10 Spring Bean的生命週期

Spring IOC 容器可以管理 Bean 的生命週期,Spring 允許在 Bean 生命週期的特定點執行定製的任務
Spring IOC 容器對 Bean 的生命週期進行管理的過程
通過構造器或工廠方法創建 Bean 實例
Bean 的屬性設置值和對其他 Bean 的引用
調用 Bean 的初始化方法
Bean 可以使用了
當容器關閉時, 調用 Bean 的銷燬方法

        在 Bean 的聲明裏設置 init-method destroy-method 屬性Bean 指定初始化和銷燬方法

4.10.1 創建 Bean 後置處理器

Bean 後置處理器允許在調用初始化方法前後對 Bean 進行額外的處理。
Bean 後置處理器對 IOC 容器裏的所有 Bean 實例逐一處理而非單一實例. 其典型應用是檢查 Bean 屬性的正確性或根據特定的標準更改 Bean 的屬性
Bean 後置處理器而言,需要實現  org.springframework.beans.factory.config.BeanPostProcessor 接口在初始化方法被調用前後,Spring 將把每個 Bean 實例分別傳遞給上述接口的以下兩個方法:
@Override
//bean:Bean實例本身
//beanName:IOC容器配置的bean的名字
//返回值:是實際上返回給用戶的那個bean,注意:可以在下面的兩個方法中修改返回的bean,甚至返回一個新的bean。
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

添加 Bean 後置處理器後 Bean 的生命週期:

Spring IOC 容器對 Bean 的生命週期進行管理的過程:
通過構造器或工廠方法創建 Bean 實例
Bean 的屬性設置值和對其他 Bean 的引用
Bean 實例傳遞給 Bean 後置處理器的 postProcessBeforeInitialization 方法
調用 Bean 的初始化方法(init-method)
Bean 實例傳遞給 Bean 後置處理器的 postProcessAfterInitialization方法
Bean 可以使用了
當容器關閉時, 調用 Bean 的銷燬方法(destory-method)

 

後置處理器的配置:

<!-- 配置 bean 後置處理器: 不需要配置 id 屬性, IOC 容器會識別到他是一個 bean 後置處理器, 並調用其方法 -->
	<bean class="com.atguigu.spring.ref.MyBeanPostProcessor"></bean>

 

4.11 實現 FactoryBean 接口在 Spring IOC 容器中配置 Bean

Spring 中有兩種類型的 Bean, 一種是普通Bean,另一種是工廠Bean,FactoryBean.
工廠 Bean 跟普通Bean不同其返回的對象不是指定類的一個實例其返回的是該工廠 Bean getObject 方法所返回的對象。
package org.springframework.beans.factory;

public interface FactoryBean<T> {
    T getObject() throws Exception;

    Class<?> getObjectType();

    boolean isSingleton();
}

 

5、Spring註解使用

5.1 基於註解的方式配置Bean

組件掃描(component scanning):Spring 能夠classpath 下自動掃描, 偵測和實例化具有特定註解的組件.
特定組件包括:
@Component: 基本註解, 標識了一個受 Spring 管理的組件
@Respository: 標識持久層組件
@Service: 標識服務層(業務層)組件
@Controller: 標識表現層組件
對於掃描到的組件, Spring 有默認的命名策略: 使用非限定類名, 第一個字母小寫. 也可以在註解中通過 value 屬性值標識組件的名稱

 

當在組件類上使用了特定的註解之後, 還需要在 Spring 的配置文件中聲明 <context:component-scan>
base-package 屬性指定一個需要掃描的基類包Spring 容器將會掃描這個基類包裏及其子包中的所有類.
當需要掃描多個包時, 可以使用逗號分隔。
如果僅希望掃描特定的類而非基包下的所有類,可使用 resource-pattern 屬性過濾特定的類,示例:
<!-- 配置自動掃描的包: 需要加入 aop 對應的 jar 包 -->
	<context:component-scan base-package="com.atguigu.spring.annotation"
		resource-pattern="repository/*.class"
	></context:component-scan>

<context:include-filter> 子節點表示要包含的目標類

<context:exclude-filter> 子節點表示要排除在外的目標
<context:component-scan> 下可以擁有若干個 <context:include-filter> <context:exclude-filter> 子節點

 

<context:include-filter> <context:exclude-filter> 節點支持多種類型的過濾表達式:

5.2 註解來進行組件裝配

<context:component-scan> 元素還會自動註冊 AutowiredAnnotationBeanPostProcessor (Bean的後置處理器)實例, 該實例可以自動裝配具有 @Autowired @Resource @Inject註解的屬性

5.2.1 @Autowired 自動裝配 Bean

@Autowired 註解自動裝配具有兼容類型的單個 Bean屬性
構造器, 普通字段(即使是非 public), 一切具有參數的方法都可以應用@Authwired 註解
默認情況下, 所有使用 @Authwired 註解的屬性都需要被設置. Spring 找不到匹配的 Bean 裝配屬性時, 會拋出異常, 若某一屬性允許不被設置, 可以設置 @Authwired 註解的 required 屬性爲 false
默認情況下, IOC 容器裏存在多個類型兼容的 Bean , 通過類型的自動裝配將無法工作. 此時可以在 @Qualifier 註解裏提供 Bean 的名稱. Spring 允許對方法的入參標註 @Qualifiter 已指定注入 Bean 的名稱
@Authwired 註解也可以應用在數組類型的屬性上, 此時 Spring 將會把所有匹配的 Bean 進行自動裝配.
@Authwired 註解也可以應用在集合屬性, 此時 Spring 讀取該集合的類型信息, 然後自動裝配所有與之兼容的 Bean.
@Authwired 註解用java.util.Map 上時, 若該 Map 的鍵值爲 String, 那麼 Spring 將自動裝配與之 Map 值類型兼容的 Bean, 此時 Bean 的名稱作爲鍵值

 

5.2.2 @Resource @Inject 自動裝配 Bean

Spring 還支持 @Resource @Inject 註解,這兩個註解和 @Autowired 註解的功用類似
@Resource 註解要求提供一個 Bean 名稱的屬性,若該屬性爲空,則自動採用標註處的變量或方法名作爲 Bean 的名稱
@Inject @Autowired 註解一樣也是按類型匹配注入的 Bean, 但沒有 reqired 屬性
建議使用 @Autowired 註解

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章