基礎(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)方法
