Spring - IOC

基礎(chǔ)

bean.xml
定義需要實例化對象的全限定類名以及類之間依賴關(guān)系描述

BeanFactory
IOC容器,通過反射技術(shù)來實例化對象并維護對象之間的依賴關(guān)系

Spring框架IOC實現(xiàn)

純xml和xml+注解模式下IOC容器啟動方式:

JavaSE應(yīng)用
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

ApplicationContext applicationContext = new FileSystemXmlApplicationContext("c:/bean.xml");
JavaWeb應(yīng)用
通過監(jiān)聽器 ContextLoaderListener 去加載xml

純注解模式下IOC容器啟動方式:

JavaSE應(yīng)用
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
JavaWeb應(yīng)用
通過監(jiān)聽器 ContextLoaderListener 去加載xml

TIPS:學習注解的技巧,找xml中標簽和注解的一一對應(yīng)關(guān)系即可

BeanFactory和ApplicationContext

BeanFactory是Spring框架中IOC容器的頂層接口,它只是用來定義一些基礎(chǔ)功能,定義一些基礎(chǔ)規(guī)范,而ApplicationContext是它的一個子接口,所以ApplicationContext是具備BeanFactory提供的全部功能。
通常我們稱BeanFactory為SpringIOC的基礎(chǔ)容器,ApplicationContext是容器的高級接口,比BeanFactory要擁有更多的功能,例如國際化支持和資源訪問等等

啟動IoC容器的方式

  • Java環(huán)境下啟動IoC容器
  • ClassPathXmlApplicationContext:從類的根路徑下加載配置文件(推薦使用)
  • FileSystemXmlApplicationContext:從磁盤路徑上加載配置文件
  • AnnotationConfigApplicationContext:純注解模式下啟動Spring容器
  • Web環(huán)境下啟動IoC容器
  • 從xml啟動容器
<web-app>
 <display-name>Archetype Created Web Application</display-name>
 <!-- 配置Spring ioc容器的配置文件 -->
 <context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath:applicationContext.xml</param-value>
 </context-param>
 <!-- 使用監(jiān)聽器啟動Spring的IOC容器 -->
 <listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
</web-app>
  • 從配置類啟動容器
<web-app>
 <display-name>Archetype Created Web Application</display-name>
 <!-- 告訴ContextLoaderListener我們使用注解的方式啟動IOC容器 -->
 <context-param>
   <param-name>contextClass</param-name>
   <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
 </context-param>
 <!-- 配置啟動類的全限定類名 -->
 <context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>com.hooda.config.SpringConfig</param-value>
 </context-param>
 <!-- 使用監(jiān)聽器啟動Spring的IOC容器 -->
 <listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
</web-app>

實例化Bean的三種方式

  • 方式一:使用無參構(gòu)造函數(shù)
<bean id="userService" class="com.hooda.service.UserServiceImpl"/>
  • 方式二:使用靜態(tài)方法創(chuàng)建
public class DefinedBeanFactory {
  public static UserService getUserService() {
    return new UserServiceImpl();
  }
}
<bean id="userService" class="com.hooda.factory.DefinedBeanFactory" factory-method="getUserService"/>
  • 方式三:使用實例化方法創(chuàng)建
public class DefinedBeanFactory {
  public UserService getUserService() {
    return new UserServiceImpl();
  }
}
<bean id="definedBeanFactory" class="com.hooda.factory.DefinedBeanFactory"/>
<bean id="userService" factory-bean="definedBeanFactory" factory-method="getUserService"/>

Bean的屬性及生命周期

  • 標簽屬性
    Id屬性:用于給bean提供一個唯一標識。在一個標簽內(nèi)部,標識必須唯一
    class屬性:用于指定創(chuàng)建Bean對象的全限定類名
    name屬性:用于給Bean提供一個或多個名稱,多個名稱用空格分割
    factory-bean屬性:用于指定創(chuàng)建當前Bean對象的工廠Bean的唯一標識。當指定了此屬性后,class屬性失效
    factory-method屬性:用于指定創(chuàng)建當前Bean對象的工廠方法,如配合factory-bean屬性使用,則class屬性失效;如配合class屬性使用,則方法必須是static的
    scope屬性:用于指定Bean對象的作用范圍,通常情況下就是singleton。當要用到多例模式時,可以配置為prototype
    init-method屬性:用于指定Bean對象的初始化方法,此方法會在Bean對象裝配后調(diào)用。必須是無參方法
    destory-method屬性:用于指定Bean對象的銷毀方法,此方法會在Bean對象銷毀前執(zhí)行。他只能為scope是singleton時起作用
  • 生命周期
    singleton
    單例模式的bean對象生命周期和容器一樣
    prototype
    多例模式的bean對象,Spring框架只負責創(chuàng)建,不負責銷毀
  • 構(gòu)造注入(constructor-arg)
    name:用于給構(gòu)造函數(shù)中指定名稱的參數(shù)賦值
    index:用于給構(gòu)造函數(shù)中指定索引位置的參數(shù)賦值
    value:用于指定基本類型或者String類型的數(shù)據(jù)
    ref:用于指定其他Bean類型的數(shù)據(jù),填寫的是其他Bean唯一標識
  • set注入(property)
    name:指定注入時調(diào)用的set方法名稱
    value:指定注入的數(shù)據(jù),支持基本類型或者String類型
    ref:指定引用的數(shù)據(jù),支持其他Bean類型,寫的是其他Bean唯一標識
  • DI注解實現(xiàn)方式
    @Autowired:Spring提供的注解,默認按照類型注入。如果出現(xiàn)一個類型有多個Bean值時,需要配合@Qualifier使用
    @Resource:J2EE提供的注解,默認按照name自動注入。如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到拋異常;如果指定了name,則從上下文中查找id匹配,找不到拋異常;如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或找到多個都會拋異常;
    TIPS:@Resource在JDK11中已經(jīng)移除,如需使用,單獨引入jar包
  • 純注解
    @Configutation:表明當前類是一個配置類
    @ComponentScan:替代context:component-scan
    PropertySource:引入外部數(shù)學配置文件
    @Import:引入其他配置類
    @Value:對變量賦值,可以直接賦值,也可以使用${}讀取資源配置文件中的信息
    @Bean:將方法返回對象加入SpringIOC容器

Spring IOC高級特性

lazy-init延遲加載
ApplicationContext容器默認在啟動服務(wù)時將所有singleton-bean進行實例化。設(shè)置lazy-init為true的bean將不會在ApplicationContext啟動時提前被實例化,而是第一次向容器通過getBean獲取時實例化
如果一個設(shè)置了立即加載的bean1引用了一個延遲加載的bean2,那么bean1在容器啟動時被實例化,而bean2由于被bean1引用,所以也會被實例化
如果一個bean的scope屬性為prototype時,即使設(shè)置了lazy-init=false,容器啟動時也不會實例化bean,而是調(diào)用時實例化。
FactoryBean&BeanFactory
Spring中Bean有兩種,一種是普通Bean,一種是工廠Bean(FactoryBean),F(xiàn)actoryBean可以生成某一個類型的Bean實例,也就是說我們可以借助于它自定義Bean的創(chuàng)建過程。

// 可以讓我們自定義Bean的創(chuàng)建過程(完成復雜Bean的定義)
public interface FactoryBean<T> {
    // 返回FactoryBean創(chuàng)建的Bean實例,如果isSingleton=true,則該實例會放到Spring容器的單利對象池中
    @Nullable
    T getObject() throws Exception;

    // 返回FactoryBean創(chuàng)建的Bean類型
    @Nullable
    Class<?> getObjectType();

    // 返回作用域是否單例
    default boolean isSingleton() {
        return true;
    }
}

// 例
public class Company {
    private name, address, scale;
}
public class CompanyFactoryBean implements FactoryBean<Company> {
    private String companyInfo;
    
    @Override
    public Company getObject() throws Exception {
        // 模擬創(chuàng)建復雜對象
        Company company = new Company();
        company.setName(companyInfo.split(",")[0]);
        ...
        return company;
    }
    @Override
    public Class<?> getObjectType() {
        return Company.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}
// xml配置
<bean id="companBean" class="CompanyFactoryBean">
    <property name="companyInfo" value="xx,xx,1"/>
</bean>
// 獲取CompanyFactoryBean產(chǎn)生的對象
Object companyBean = applicationContext.getBean("companyBean");
// 獲取CompanyFactoryBean對象本身
Object companyBean = applicationContext.getBean("&companyBean");

后置處理器
Spring提供了兩種后置處理bean的擴展接口,分別為BeanPostProcessor和BeanFactoryPostProcessor,兩者在使用上是有所區(qū)別的。
BeanPostProcessor是針對Bean級別的處理,在Bean對象實例化之后。

public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; }
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }
}

該接口提供了兩個方法,分別在Bean的初始化方法前后執(zhí)行,具體初始化方法指的什么方法,類似我們在定義bean時,init-method所指定的方法
定義一個類實現(xiàn)了BeanPostProcessor,默認會對整個Spring容器中所有bean進行處理。如果要對某個bean處理,可以通過beanName來判斷我們將要處理的具體bean
BeanFactoryPostProcessor是針對BeanFactory級別的處理,是對整個Bean的工廠進行處理。典型應(yīng)用:PropertyPlaceHolderConfigurer

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

此接口只提供了一個方法,方法參數(shù)為ConfigurableListableBeanFactory,該參數(shù)類型定義了一些方法


我們可以根據(jù)getBeanDefinition方法找到BeanDefinition對象,然后對定義的屬性進行修改。
TIPS:調(diào)用BeanFactoryPostProcessor方法時bean還沒有實例化,此時bean剛被解析成BeanDefinition對象

Bean生命周期關(guān)鍵時機點

Bean對象構(gòu)造函數(shù)調(diào)用時機&InitializingBean之a(chǎn)fterPropertiesSet
在AbstractApplicationContext.refresh()的finishBeanFactoryInitialization(BeanFactory)處
BeanFactoryPostProcessor初始化和調(diào)用時機
BeanFactoryPostProcessor初始化和調(diào)用都在AbstractApplicationContext.refresh()的invokeBeanFactoryPostProcessor(beanFactory)
BeanPostProcessor初始化和調(diào)用時機
BeanPostProcessor初始化在AbstractApplicationContext.refresh()的registerBeanPostProcessors(beanFactory)處
postProcessBeforeInitialization&postProcessAfterInitialization都在AbstractApplicationContext.refresh()的finishBeanFactoryInitialization(beanFactory)處
Spring IOC容器初始化主流程

public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        // 第一步 刷新前的預處理
        prepareRefresh();
        /*
          第二步 獲取BeanFactory,默認實現(xiàn)DefaultListableBeanFactory
          加載BeanDefinition并注冊到BeanDefinitionRegistry中
        */
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 第三步 BeanFactory的預準備工作(進行一些設(shè)置,比如Context的類加載器等)
        prepareBeanFactory(beanFactory);
        try {
            // 第四步:BeanFactory準備工作完成后進行的后置處理工作
            postProcessBeanFactory(beanFactory);
            // 第五步:實例化并調(diào)用實現(xiàn)了BeanFactoryPostProcessor接口的Bean
            invokeBeanFactoryPostProcessors(beanFactory);
            // 第六步:注冊BeanPostProcessor(Bean的后置處理器),在創(chuàng)建bean前后執(zhí)行
            registerBeanPostProcessors(beanFactory);
            // 第七步:初始化MessageSource組件(做國際化功能;消息綁定;消息解析)
            initMessageSource();
            // 第八步:初始化事件派發(fā)器
            initApplicationEventMulticaster();
            // 第九步:子類重寫這個方法,在容器刷新的時候可以自定義邏輯
            onRefresh();
            // 第十步:注冊實現(xiàn)了ApplicationListener接口的監(jiān)聽器
            registerListener();
            /*
              第十一步:初始化所有剩下的非懶加載單例bean
              初始化創(chuàng)建非懶加載方式的單例bean(未設(shè)置屬性)
                  填充屬性
                  初始化方法調(diào)用(比如調(diào)用afterPropertiesSet方法,init-method方法)
                  調(diào)用BeanPostProcessor(后置處理器)對實例bean進行后置處理
             */
            finishBeanFactoryInitialization(beanFactory);
            // 第十二步:完成context刷新,主要是調(diào)用LifecycleProcessor的onRefresh方法,并且發(fā)布事件(ContextRefreshEvent)
            finishRefresh();
        }
        ...
    }
}

BeanFactory創(chuàng)建流程

獲取BeanFactory子流程

時序圖

BeanDefinition加載解析級注冊子流程
Resource定位:指對BeanDefinition的資源定位過程。找到定義JavaBean信息的xml文件并將其封裝成Resource對象
BeanDefinition載入:把用戶定義好的JavaBean表示為IoC容器內(nèi)部的數(shù)據(jù)結(jié)構(gòu)
step1:入口在AbstractRefreshableApplicationContext#refreshBeanFactory方法中調(diào)用了loadBeanDefinition(beanFactory)
step2:調(diào)用多個類的loadBeanDefinition方法,鏈 AbstractXmlApplicationContext -> AbstractBeanDefinitionReader -> XmlBeanDefinitionReader#doLoadBeanDefinitions,在doLoadBeanDefinition方法中執(zhí)行registerBeanDefinition(doc, resource)方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容