Spring-IOC總結

1 Spring IOC是什麼

1.1 Spring IOC是個什麼東西

IOC是用爲用戶建立、管理實例對象的。用戶須要實例對象時只須要向IOC容器獲取就好了,不用本身去建立,從而達到與具體類解耦。 php

簡單點來說就是Spring IOC就是一個Map集合,對象的名字就是集合中的key,值就是對應的對象。咱們能夠經過一個對象的名字到集合中獲取對象。java

1.2 IOC實現的流程

  1. Bean定義

Spring提供了多種方式來配置Bean定義,有Xml,JavaConfigspring

Xml:設計模式

<bean id="user" class="com.ranger.bean.User">
    <constructor-arg type="String" value="ranger"></constructor-arg>
    <constructor-arg ref="cbean"></constructor-arg>
</bean>
<bean id="car" class="com.ranger.bean.Car">
    <constructor-arg type="String" value="mazda"></constructor-arg>
</bean>
複製代碼

JavaConfig的方法,這種方式要和註解配合app

@Configuration
public class AppConfig {

    @Bean
    public Service myService() {
        return new ServiceImpl();
    }

}
複製代碼
  1. Spring讀取Bean的定義

建立SpringIOC容器的時候指定一個配置文件(xml),或者指定包掃描的路徑(JavaConfig)ui

// 經過指定classpath下的配置文件 
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springcontext.xml");

複製代碼

或者使用JavaConfig的方式spa

AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(AppConfig.class);

annotationConfigApplicationContext.refresh();
System.out.println(annotationConfigApplicationContext.getBean(User.class));

複製代碼

2. 從入口ApplicationContext開始

大概介紹每一個父接口對應的職責prototype

  • EnvironmentCapable:獲取環境變量相關的參數
  • HierarchicalBeanFactory:提供父子容器功能
  • ListableBeanFactory:BeanFactory的實現
  • ApplicationEventPublisher:時間的發佈
  • ResourcePatternResolver:加載Resource文件
  • MessageSource:提供國際化功能

ApplicationContext中定義的方法線程

ApplicationContext中定義了本身的幾個方法,對這幾個方法作簡單的介紹:設計

  • getApplicationName:返回容器所屬的應用的名稱, 默認返回空字符串
  • getAutowireCapableBeanFactory:暴露當前容器的AutowireCapableBeanFactory
  • getDisplayName:返回一個友好的容器名字
  • getId:返回當前容器的惟一Id
  • getParant:返回父容器,沒有的話返回null
  • getStartupDate:返回容器第一次加載完成的時間戳

2.1 Application的子類

2.1.1 AbstarctApplicationContext 的子類

咱們先從ApplicationContext後面的子孫開始

從上面的類繼承圖能夠看出,AbastractApplicationContext 後面有兩個兒子,分別是 GernericApplicationContextAbstractRefreshableApplicationContext.這兩個大兒子分別都持有了BeanFactory實例,這兩個兒子全部的對於BeanDefiniton的註冊,Bean的實例化都是基於這個BeanFactory實例的

它們分紅了兩大派系

  • AbstractRefreshableApplicationContext:這個類在每次調用refresh方法的時候都會產生一個新的beanfactory實例(一般是,可是不是必須的)。這個應用上下文會經過一系列的配置文件去加載BeanDefinition。在調用refresh方法的時候纔會建立內部持有的BeanFacoty實例(能夠參見該類中的refreshBeanFactory方法)

  • GenericApplicationContext:這個類內部持有惟一的一個DefaultListableBeanFactory實例,並且相較於其它ApplicationContext的實現類,這個類在建立的時候就會有一個BeanFactory的實例,意思就是在refresh方法調用前,內部持有的BeanFactory實例就已經建立,且這個類從開始到最終都是一個BeanFacoty實例。

GenericApplicationContext實現了BeanDefinitionRegistry這個接口,這個接口乾啥的呢,看名字是BeanDefinition的註冊什麼東東,沒錯,這個就是用來添加刪除BeanDefiniton的。GenericApplicationContext也還有幾個兒子,後面會簡單分析一下他們的不一樣。

2.1.2 AbstractApplicationContext抽象類簡單說明

上面都提到了refresh方法,這個方法是AbastractApplicationContext實現的,用於配置Context.該類使用了模板方法模式,不少方法都留給了子類去實現。

在AbstractApplicationContext中,實現了大多數ApplicationContext接口從BeanFactroy接口繼承來的方法。

咱們還能夠看到,AbstractApplicationContext的父類是ConfigurableApplicationContext,這個類提供了配置Context的方法,還提供了生命週期方法。

3 BeanDefinition的加載

經過上面的分析咱們看到,雖然都有一個共同的祖先叫作ApplicationContext,可是不一樣的子孫仍是有不一樣的加載BeanDefinition的方法,可是其它方面都是同樣的,SpringIOC中最重要的方法refresh就在他們共同的老爸AbstractApplicationContext中。refresh方法會根據BeanDefinition來建立Bean對象(除開lazy loading)

咱們就從咱們常見的ClasspathXmlApplicationContext開始分析:

如今項目中包含上面圖中的類和配置,咱們編寫一個主方法:

public class Application {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springcontext.xml");

        Person person = applicationContext.getBean(Person.class);
        System.out.println(person);

    }
}

複製代碼

springcontext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" default-lazy-init="true" default-init-method="" default-destroy-method="">

    <bean id="car" class="com.ranger.spring.ioc.bean.Car">
        <constructor-arg name="brand" value="mazda"></constructor-arg>
    </bean>

    <bean id="person" class="com.ranger.spring.ioc.bean.Person">
        <property name="car" ref="car"></property>
    </bean>
</beans>

複製代碼

調試啓動Application類的main方法

由於最終BeanDefinition的註冊都是在DefaultListableBeanFactory 中完成的,因此在registerBeanDefinition(String beanName, BeanDefinition beanDefinition)打斷點就能看到下面的調用棧

從線程調用棧的下面向上看。

獲得一個基本的流程:

這裏面的解析Document獲取BeanDefinition也比較複雜,若是有興趣能夠去看看。

那麼GenericXmlApplicationContext加載BeanDefinition的流程是否是也和上面同樣呢。

能夠看到流程是同樣的。

只是GenericXmlApplicationContext會先調用load來加載BeanDefinition,而後調用refresh完成配置。

而ClasspathXmlApplicationContext會在refresh方法調用的時候完成BeanDefinition的加載。

4 bean工廠-DefaultListableBeanFactory

經過前面的分析咱們能夠看到,ApplicationContext的大部分操做其實都是基於DefaultListableBeanFactory來完成的。

DefaultListableBeanFactory是BeanFactory的一個實現類

如今咱們來認識一下它:

  1. 先看看最上面的祖先BeanFactory

經過讀源碼的doc,

  • 這個接口是spring bean容器的根接口,它有一些爲了提供特定功能的子接口ListableBeanFactory和ConfigurableBeanFactory

  • 實現這個接口的對象持有一系列的 bean definitions,每一個bean definition 都有一個惟一的字符串名字。返回的Bean能夠是單例的,也能夠是獨立的(每次都要建立),具體返回什麼類型取決於applicationcontext的配置。

  • BeanFactory經過依賴注入來完成配置,一般的手段是用setter或者constructor

  • 一般狀況BeanFactory加載的BeanDefinition保存在一個配置資源中,好比XML文件。可是具體存儲在哪兒是沒有限制的,好比LDAP,XML,properties等等。

  • HierarchicalBeanFactory會先從本上下文找,找不到從父BeanFactory找,且本工廠實例中的bean會覆蓋父工廠

  • BeanFactory的實現類應該儘量支持bean的生命週期方法,好比BeanNameAware,BeanClassLoaderAware,等等。

    對於這些生命週期方法的支持,BeanFacoty沒有給出抽象的接口,須要實現類本身去實現

BeanFactory的源碼:

public interface BeanFactory {

    // 用來區分FactoryBean和其產生的對象
	String FACTORY_BEAN_PREFIX = "&";

	// 經過BeanName獲取Bean
	Object getBean(String name) throws BeansException;

	// 經過beanName和bean 的Class類型來獲取Bean
	<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

	// 增長獲取bean的參數
	Object getBean(String name, Object... args) throws BeansException;

	// 經過類型獲取
	<T> T getBean(Class<T> requiredType) throws BeansException;

	// 和上面同樣的道理
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	// 判斷是否包含某個Bean
	boolean containsBean(String name);

	// bean是不是單例
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	// bean是不是prototype
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	//查詢指定了名字的Bean的Class類型是否與指定類型匹配
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	// 同上
	boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	//獲取指定名字bean的Class類型
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    // 獲取bean的別名
	String[] getAliases(String name);

}
複製代碼

BeanFactory 有三個子類接口:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory,還有一個實現類SimpleJndiBeanFactory。

這裏對於BeanFactory的體系介紹就先不說了,太多了,單獨寫吧。

5 後記

ApplicationContext的體系很大,重要抓住了核心幾個比較重要的幾個類:AbstractApplicationContext,以及它的兩個大兒子 GenericApplicationContextAbstractRefreshableApplicationContext。大部分功能都在這裏面實現了。

兩個大兒子生下的兒子都是基於他們作了一些擴展。

閱讀代碼能夠發現,ApplicationContext不少的方法都留到了子類去實現,這裏用到了模板方法設計模式。

最終對於註冊BeanDefinition和基於BeanDefinition建立bean實例都是歸結到了DefaultListableBeanFactory中。

前面對Spring容器的體系作了總體的瞭解,接下來會分析bean的建立。

相關文章
相關標籤/搜索