就是要你懂Spring-IOC

爲何引入IOC?

class Programer {
    Computer computer = new Mac2015();
    private void work() {
        computer.help();
    }
}
複製代碼

此時有一個問題就是computer和programer耦合在一塊兒,這個programer不具有擴展性(它只會用mac2015),若是此時公司換了一批電腦Mac2016,那麼須要從新建立一個新的程序員類,這顯然是不合理的。
從設計的角度來說,類自己就是定義的一個模板亦或是一種抽象,若是抽象和具體的實現綁定或者耦合在一塊兒,那麼就不是純粹的抽象了。 這也違背了設計模式中的don't call me法則。因此這個時候要把computer和programer解耦,解耦的方法很簡單。 computer的具體實現由調用方指定就ok,而不是在類內部自行指定。那麼類就須要暴露一些接口供外界實現注入功能。
常見的方法有三種java

  • 構造函數注入
  • set注入
  • 接口注入

這就是IOC的基本思路,而對於咱們web開發來說通常不存在調用方(其實也有不過在容器層面),因此如何保證類中屬性的動態注入,這個就要由Spring登場了
接下來,選擇何種注入方式以及須要注入哪些屬性是怎麼通知到Spring的呢? 常見的方法有兩種
程序員

  • 註解
  • 配置文件(xml/properties)

這裏多提一句,bean的實例化方式能夠是普通的構造器構造也能夠是工廠方法構造 下面這個是經過靜態工廠注入的方式web

<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
複製代碼

class指向的是包含工廠方法的類,並不是工廠方法返回的類 下面這個是經過普通工廠注入的方式spring

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
複製代碼

IOC容器分類

  • BeanFactorysql

    默認lazy-load,因此啓動較快 經常使用的一個實現類是DefaultListableBeanFactory,該類還實現了BeanDefinitionRegistry接口,該接口擔當Bean註冊管理的角色 registerBeanDefinition#BeanDefinitionRegistry bean會以beanDefinition的形式註冊在BeanDefinitionRegistry中 BeanFactory接口中只定義了一些關於bean的查詢方法,而真正的工做須要其子類實現的其餘接口定義~apache

  • ApplicationContext設計模式

    構建於BeanFactory基礎之上,非lazy,啓動時間較長,除了BeanFactory的基礎功能還提供了一些額外的功能(事件發佈、國際化信息支持等)緩存

手動與自動

  • 基於配置文件的注入 咱們稱他爲手動app

    <bean> <name="propertyName" ref="otherBean">ide

  • 基於註解的注入 咱們稱他爲全自動

    @Conponet/@Autowire/@Resource + <componet-scan>

  • 還有註解+配置的半自動

    <bean> + @autowire/@resource

關於xml中的bean

通常最頂層是beans標籤 beans標籤有幾個獨特的屬性,對包含的全部bean生效

  • default-lazy-init

    全部bean是否懶加載

  • default-autowire

    全部bean內部注入的方式no(默認)/byName/byType/constrctor/autodetect
    這裏byName/byType ≈ @resource/@autowire

  • default-dependency-check

    是否進行依賴檢查 none(默認)

  • default-init-method

    全部bean的初始化方法好比init

  • default-destroy-method

    全部bean的銷燬方法好比destroy

基本上beans的全部屬性均可以對應到bean中,這裏提一下bean的scope屬性 關於Singleton和Prototype singleton類型的bean的存活時間和容器同樣長 prototype類型的bean的生命週期不禁容器維護,容器再也不擁有當前對象的引用,任由其自生自滅

想起當初解決的一個問題,持有對象的引用(new/reflect),可是這個對象未進行注入,如何利用Spring對其進行注入?

applicationContext.getAutowireCapableBeanFactory().
     autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_NO, true);

複製代碼

Bean的生命週期

先放結論,後面有具體的代碼論證(圖文結合效果更佳)

實踐是檢驗真理的惟一標準,舉個例子

public class Car implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
    private String carName;
    private BeanFactory beanFactory;
    private String beanName;

    public Car() {
        System.out.println("bean的構造函數");
    }

    public void setCarName(String carName) {
        System.out.println("屬性注入");
        this.carName = carName;
    }

    // 這是BeanFactoryAware接口方法
    public void setBeanFactory(BeanFactory arg0) throws BeansException {
        System.out
                .println("BeanFactoryAware.setBeanFactory()");
        this.beanFactory = arg0;
    }

    // 這是BeanNameAware接口方法
    public void setBeanName(String arg0) {
        System.out.println("BeanNameAware.setBeanName()");
        this.beanName = arg0;
    }

    // 這是InitializingBean接口方法
    public void afterPropertiesSet() throws Exception {
        System.out
                .println("InitializingBean.afterPropertiesSet()");
    }

    // 這是DiposibleBean接口方法
    public void destroy() throws Exception {
        System.out.println("DiposibleBean.destory()");
    }

    // 經過<bean>的init-method屬性指定的初始化方法
    public void myInit() {
        System.out.println("<bean>的init-method屬性指定的初始化方法");
    }

    // 經過<bean>的destroy-method屬性指定的初始化方法
    public void myDestory() {
        System.out.println("<bean>的destroy-method屬性指定的初始化方法");
    }
}
複製代碼
public class MyBeanPostProcessor implements BeanPostProcessor {

    public MyBeanPostProcessor() {
        super();
        System.out.println("BeanPostProcessor構造函數");
    }

    public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException {
        if (arg1.equals("car")) {
            System.out
                    .println("BeanPostProcessor.postProcessAfterInitialization()");
        }
        return arg0;
    }

    public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException {
        if (arg1.equals("car")) {
            System.out
                    .println("BeanPostProcessor.postProcessBeforeInitialization()");
        }
        return arg0;
    }
}
複製代碼
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public MyBeanFactoryPostProcessor() {
        super();
        System.out.println("BeanFactoryPostProcessor構造函數");
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
        System.out.println("BeanFactoryPostProcessor.postProcessBeanFactory()");
        System.out.println("我如今能夠修改beanDefination可是我這裏就不修改了");
    }
}
複製代碼
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

    public MyInstantiationAwareBeanPostProcessor() {
        super();
        System.out
                .println("InstantiationAwareBeanPostProcessorAdapter構造函數");
    }

    // 接口方法、實例化Bean以前調用
    @Override
    public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
        if(beanName.equals("car")) {
            System.out
                    .println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()");
        }
        return null;
    }

    // 接口方法、實例化Bean以後調用
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if(beanName.equals("car")) {
            System.out
                    .println("InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()");
        }
        return true;
    }
    
    // 接口方法、設置某個屬性時調用
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        if(beanName.equals("car")) {
            System.out
                    .println("InstantiationAwareBeanPostProcessor.postProcessPropertyValues()");
        }
        return pvs;
    }
}
複製代碼
<bean id="beanPostProcessor" class="test.MyBeanPostProcessor"/>
<bean id="instantiationAwareBeanPostProcessor" class="test.MyInstantiationAwareBeanPostProcessor"/>
<bean id="beanFactoryPostProcessor" class="test.MyBeanFactoryPostProcessor"/>
<bean id="car" class="test.Car" init-method="myInit" destroy-method="myDestory" scope="singleton" p:carName="BMW"/>
複製代碼

下面來見證下整個流程

public static void main(String[] args) {
        System.out.println("開始啓動容器");
        ApplicationContext factory = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        System.out.println("容器初始化成功");
        Car car = factory.getBean("car",Car.class);
        System.out.println("開始關閉容器");
        ((ClassPathXmlApplicationContext)factory).registerShutdownHook();
    }
複製代碼

最終的console輸出爲:

開始啓動容器
BeanFactoryPostProcessor構造函數
BeanFactoryPostProcessor.postProcessBeanFactory()
我如今能夠修改beanDefination可是我這裏就不修改了
InstantiationAwareBeanPostProcessorAdapter構造函數
BeanPostProcessor構造函數
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
bean的構造函數
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
InstantiationAwareBeanPostProcessor.postProcessPropertyValues()
屬性注入
BeanNameAware.setBeanName()
BeanFactoryAware.setBeanFactory()
BeanPostProcessor.postProcessBeforeInitialization()
InitializingBean.afterPropertiesSet()
<bean>的init-method屬性指定的初始化方法
BeanPostProcessor.postProcessAfterInitialization()
容器初始化成功
開始關閉容器
DiposibleBean.destory()
<bean>的destroy-method屬性指定的初始化方法
複製代碼

1)容器啓動階段
容器須要依賴某些工具類(BeanDefinitionReader)對加載的Configuration MetaData進行解析和分析,並將分析後的信息編組爲相應的BeanDefinition,最後把這些保存了bean定義必 要信息的BeanDefinition,註冊到相應的BeanDefinitionRegistry,這樣容器啓動工做就完成了。
BeanDefinitionReader
1)用來和配置文件打交道
2)負責從對應的配置文件中讀取內容並將bean映射到BeanDefinition
3)而後將BeanDefinition註冊到BeanDefinitionRegistry中
eg:PropertiesBeanDefinitionReader、XmlBeanDefinitionReader

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanDefinitionRegistry);
reader.loadBeanDefinitions("classpath:xxx");
return (BeanFactory) beanDefinitionRegistry;
複製代碼

惟一能插手容器啓動階段的大佬-----BeanFactoryPostProcessor 在容器實例化對象以前,對註冊到容器的BeanDefinition信息進行修改
下面是幾個比較典型的BeanFactoryPostProcessor

  • PropertyPlaceholderConfigurer

在實例化bean前,將bean配置中的佔位符替換爲實際值

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
複製代碼
# jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
複製代碼

在運行時,佔位符內的內容會被替換爲屬性文件中的匹配的value,這個工做就是由PlaceholderConfiguer完成的,不難看出這個類應該實現了BeanFactoryPostProcessor

  • CustomEditorConfigurer

即容器從XML格式的文件中讀取的都是字符串形式,最終應用程序倒是由各類類型的對象所構成。 要想完成這種由字符串到具體對象的轉換(無論這個轉換工做最終由誰來作),都須要這種轉換規則 相關的信息,而CustomEditorConfigurer就是幫助咱們傳達相似信息的。
以下咱們須要將String類型的2018/07/27轉爲date

<bean id="dateFoo" class="...DateFoo">
    <property name="date">
        <value>2018/07/27</value>
    </property>
</bean>
複製代碼

用戶能夠自定義CustomEditorConfigurer,來指定string到特定類型轉換到邏輯

  • BeanFactoryPostProcessor的實際應用例子

以前項目中使用JUNIT進行單元測試,可是每次單元測試都要加載全部的bean,項目規模若是較大,那麼單元測試啓動須要很長時間,爲了解決這個問題開發了一個簡單的BeanFactoryPostProcessor,原理很簡單就是將全部的bean都設置爲懶加載模式,單元測試中用到哪一個實例化哪一個

public class LazyBeanFactoryProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        DefaultListableBeanFactory fac = (DefaultListableBeanFactory) beanFactory;
        Map<String, AbstractBeanDefinition> map = (Map<String, AbstractBeanDefinition>) ReflectionTestUtils.getField(fac, "beanDefinitionMap");
        for (Map.Entry<String, AbstractBeanDefinition> entry : map.entrySet()) {
            entry.getValue().setLazyInit(true);
        }
    }
}
複製代碼

2)Bean實例化階段 1.InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(Class beanClass,String beanName) 在執行Bean的構造器以前,若是有InstantiationAwareBeanPostProcessor那麼會先執行它的postProcessBeforeInstantiation()若是此時返回的bean不爲null,那麼不會再繼續執行後續的Bean流程,只執行postProcessAfterInitialization(),形成了"短路" 有一點須要注意的在提到InstantiationAwareBeanPostProcessor的方法的時候最好指定入參,由於它有好幾個相同名字入參不一樣的方法,容易發生混淆

2.對bean進行實例化 容器採用策略模式來決定採用何種方式初始化bean實例(反射 or CGLIB默認) 若是配置的bean有look-up or replace此時須要使用CGLIB建立代理類

3.InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(Class beanClass,String beanName)

4.InstantiationAwareBeanPostProcessor.postProcessPropertiesValues(PropertyValues pvs,PropertyDescriptor[] pds, Object bean, String beanName)

5.對bean進行依賴注入 實例化後的產物是BeanWrapper實例,它是對bean的包裹,接下來對這個包裹設置屬性值 Ps藉助Wrapper能夠用統一的方式對對象屬性進行注入,BeanWrapper會使用PropertyEditor進行屬性的類型轉換

6.檢查是否實現Aware接口(BeanFactory獨有) BeanNameAware:將BeanName設置到當前實例中 BeanClassLoaderAware:將加載當前bean的ClassLoader設置到當前實例中 BeanFactoryAware:將BeanFactory設置到當前實例中

7.BeanPostProcessor.postProcessBeforeInitialization 不少ApplicationContext獨有的Aware接口也是經過BeanPostPorcessor實現屬性注入的

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher 
(this.applicationContext); }
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
   ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }
    return bean;
}
複製代碼

8.InititalizingBean.afterPropertiesSet()

9.init-method

10.BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName)

11.容器初始化成功,正常執行業務邏輯

12.DisposableBean.destroy()

13.destroy-method

循環依賴

Spring對於單例的bean之間的循環依賴會進行處理(Set注入),換言之對於單例構造器注入以及prototype類型的bean沒法處理循環依賴。假設A依賴B,B又依賴於A,在實例化A的時候會將半成品的A放到緩存中,再去實例化所依賴的B,而在實例化B過程當中又須要去實例化A,此時直接將半成品的A填充到B中,就完成了循環依賴Bean的實例化。

關於BeanPostProcessor

  • BeanPostProcessor處理容器內全部符合條件的BeanDefinition
    Ps這裏的符合條件與否在覆寫的方法中自行判斷if(beanName==xxx)
  • A bean post-processor typically checks for callback interfaces or may wrap a bean with a proxy. Some Spring AOP infrastructure classes are implemented as bean post-processors in order to provide proxy-wrapping logic.
    AOP就是經過BeanPostProcessor實現偷樑換柱的
  • Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessors and beans that they reference directly are instantiated on startup, as part of the special startup phase of the ApplicationContext實現該接口的bean是很特殊的,它以及它所依賴的bean須要優先啓動,能夠將其視爲容器啓動的一個階段。並且實現了該接口的bean(或者被依賴的beans)沒法對其進行動態代理,由於動態代理自己就是經過BeanPostProcessor實現的,你能夠理解爲"大力士舉不起本身"
相關文章
相關標籤/搜索