在理解任何技術以前,我都會問本身一個問題:它的產生是爲了解決什麼樣的問題,以及如何解決這些問題?但願你能在本篇文章中找到答案……
(因爲你們對Ioc應該是常用了,因此這裏不會告訴你應該怎麼樣使用,重要的是理解思想原理,理解過程)javascript
IoC能夠說是spring最核心的部分,是spring家族任意組件的基本。Ioc 自己並不能算爲一種技術,而是一種思想,它使你從繁瑣的對象交互中解脫出來,而專一於對象自己,更進一步突出面向對象。
咱們先來回答文章開頭問題的上半部分:
咱們假設一個場景:Person(人)天天都要吃早餐(食物)。咱們能夠用以下程序表示html
public class Person {
public void eat() {
Food food = new food();
System.out.println("I eat food:{}", food.toString());
}
}複製代碼
在咱們吃飯以前必須先new food()(作飯),要否則就吃不上。
Ioc 會怎麼樣作呢java
public class Person {
private Food food;
public void eat() {
System.out.println("I eat food:{}", food.toString());
}
}複製代碼
它會在你吃的時候將食物準備好,不須要你本身作飯。由於它認爲:吃飯的人不該該身兼廚師的角色。
借用《spring 揭祕》中的漫畫再說明一下吧(由於我不會畫吃飯的漫畫)。它的意思是:穿衣服出門。若是不使用Ioc,你就得本身去取衣服穿上。用了IOC,已經有美女給你拿過來並幫你穿上(有沒有一種大款的感受)。IOC就是讓你當大款,你只須要發揮本身的特長掙錢就能夠了,其它的讓小祕來。
web
接下來的問題是如何將依賴的對象準備好呢(依賴注入),經常使用的有兩種方式:構造方法注入和setter注入(雖然你們都很熟悉了,但還請原諒我再說一下)
構造器注入,它就表明了當Person這個對象生成時,就準備好了:即不管你吃不吃飯,飯就在那裏,不離不棄spring
public Person(Food food) {
this.food = food;
}複製代碼
setter注入,有所不一樣:俺不是那麼隨便的食物,你得喊我(set)俺纔過來,有種悶騷的感受。反正我就喜歡這種……數據庫
public void setFood(Food food) {
this.food = food;
}複製代碼
但不管前提哪種注入方法,你總得有小祕來執行吧!!!so,你只須要默默地躺在那來享受,小祕帶來百般絕技!!!app
小祕絕技雖然精彩,但要實現卻並不那麼容易。它須要一系列技術要實現。首先它須要知道服務的對象是誰,以及須要爲服務對象提供什麼樣的服務。提供的服務指:要完成對象的構建(即把飯作好),將其送到服務對象即完成對象的綁定(即把飯端到我面前)。
上面的話別看糊塗了,再聲明一下,Ioc須要實現兩個技術:框架
對於這兩個方面技術的實現具備不少的方式:硬編碼(Ioc 框架都支持),配置文件(咱們的重點),註解(最潔的方式)。但不管哪一種方式都是在Ioc容器裏面實現的(咱們能夠理解爲一個大池子,裏面躺着各類各樣的對象,並能經過必定的方式將它們聯繫起來)
spring提供了兩種類型的容器,一個是BeanFactory,一個是ApplicationContext(能夠認爲是BeanFactory的擴展),下面咱們將介紹這兩種容器如何實現對對象的管理。ide
若是沒有特殊指定,默認採用延
遲初始化策略(lazy-load)。只有當客戶端對象須要訪問容器中的某個受管對象的時候,纔對 該受管對象進行初始化以及依賴注入操做。因此,相對來講,容器啓動初期速度較快,所需 要的資源有限。對於資源有限,而且功能要求不是很嚴格的場景,BeanFactory是比較合適的 IoC容器選擇。
咱們先來看一下BeanFactory類的關係圖(以下所示)
函數
bean實例化階段:
當某個bean 被 getBean()調用時
bean須要完成初時化,以及其依賴對象的初始化
若是bean自己有回調,還須要調用其相應的回調函數
從 上面咱們也能夠知道,beanDefinition(容器啓動階段)只完成bean的定義,並未完成初始化。初始是經過beanFactory的getBean()時才進行的。
Spring Ioc在初始化完成以後,給了咱們提供一些方法,讓咱們來改變一些bean的定義
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer:使咱們可能經過配置文件的形式,配置一些參數
PropertyOverrideConfigurer :則能夠覆蓋本來的bean參數
CustomEditorConfigurer :則提供類型轉換支持(配置文件都是string,它須要知道轉換成何種類型)
Bean的初始化過程:
BeanPostprocessor 能夠幫助完成在初始化bean以前或以後 幫咱們完成一些必要工做,好比咱們在鏈接數據庫以前將密碼存放在一個加密文件,當咱們鏈接數據庫以前,須要將密碼進行加載解密。只要實現 相應的接口便可
public interface BeanPostProcessor {
/** * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. * The returned bean instance may be a wrapper around the original. * @param bean the new bean instance * @param beanName the name of the bean * @return the bean instance to use, either the original or a wrapped one; if * {@code null}, no subsequent BeanPostProcessors will be invoked * @throws org.springframework.beans.BeansException in case of errors * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet */
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/** * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. * The returned bean instance may be a wrapper around the original. * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean * instance and the objects created by the FactoryBean (as of Spring 2.0). The * post-processor can decide whether to apply to either the FactoryBean or created * objects or both through corresponding {@code bean instanceof FactoryBean} checks. * <p>This callback will also be invoked after a short-circuiting triggered by a * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method, * in contrast to all other BeanPostProcessor callbacks. * @param bean the new bean instance * @param beanName the name of the bean * @return the bean instance to use, either the original or a wrapped one; if * {@code null}, no subsequent BeanPostProcessors will be invoked * @throws org.springframework.beans.BeansException in case of errors * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet * @see org.springframework.beans.factory.FactoryBean */
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}複製代碼
在完成postProcessor以後,則會看對象是否認義了InitializingBean 接口,若是是,則會調用其afterProper- tiesSet()方法進一步調整對象實例的狀態 ,這種方式並不常見。spring還提供了另一種指定初始化的方式,即在bean定義中指定init-method 。
當這一切完成以後,還能夠指定對象銷燬 的一些回調,好比數據庫的鏈接池的配置,則銷燬前須要關閉鏈接等。相應的能夠實現DisposableBean 接口或指定destroy-method
ApplicationContext 容器創建BeanFactory之上,擁有BeanFactory的全部功能,但在實現上會有所差異。我認爲差異主要體如今兩個方面:1.bean的生成方式;2.擴展了BeanFactory的功能,提供了更多企業級功能的支持。
1.bean的加載方式
BeanFactory提供BeanReader來從配置文件中讀取bean配置。相應的ApplicationContext也提供幾個讀取配置文件的方式:
ResourceLoader並不能將其當作是Spring獨有的功能,spring Ioc只是藉助於ResourceLoader來實現資源加載。也提供了各類各樣的資源加載方式:
提供國際化支持,不講了,有須要請轉至:blog.sina.com.cn/s/blog_85d7…
#4、最佳實踐
註解掃描
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="org.spring21"/>
</beans>複製代碼
component/service/controller註解
@Component
public class Person {
@Resource
private Food food;
public void setFood(Food food) {
this.food = food;
}
}複製代碼
bean的前置後置
@Component
public class Person {
@Resource
private Food food;
public setFood(Food food) {
this.food = food;
}
@PostConstruct
public void wash() {
System.out.println("飯前洗手");
}
@PreDestroy
public void brush() {
System.out.println("飯後刷牙");
}
}複製代碼