一滑进、IOC與DI
很多人學(xué)習(xí)Spring框架都是從IOC入手的, IOC(Inversion of Control)譯為“控制反轉(zhuǎn)”难礼,基于這一概念颗管,可以衍生出下面幾個(gè)問(wèn)題:
誰(shuí)控制了誰(shuí)?
控制了什么?
為什么是反轉(zhuǎn)重挑?
首先嗓化,我們來(lái)回答第一個(gè)問(wèn)題:傳統(tǒng)模式下,我們通常使用new來(lái)創(chuàng)建對(duì)象谬哀。而使用Spring刺覆,我們調(diào)用getBean(String name, Class<?> type)就可以直接獲得對(duì)象。因此史煎,IOC容器控制了對(duì)象谦屑。
那么,Spring容器控制了對(duì)象的什么呢篇梭?要回答這個(gè)問(wèn)題氢橙,我們可以直接把IOC的定義搬過(guò)來(lái):
所謂 IOC ,就是由 Spring IOC 容器來(lái)負(fù)責(zé)對(duì)象的生命周期和對(duì)象之間的關(guān)系恬偷。
為什么是反轉(zhuǎn)悍手?通過(guò)new來(lái)創(chuàng)建對(duì)象,對(duì)象的生命周期以及對(duì)象間的依賴都由程序員自己控制喉磁,這是正轉(zhuǎn)谓苟。而反轉(zhuǎn)剛好倒過(guò)來(lái),由容器來(lái)對(duì)對(duì)象進(jìn)行管理协怒,我們可以直接從容器中獲得對(duì)象及其依賴涝焙。
DI(Dependency Injection),即“依賴注入”孕暇,是指容器在運(yùn)行時(shí)決定組件之間的依賴仑撞,這是容器管理對(duì)象的另一個(gè)說(shuō)法。它并非是為系統(tǒng)添加新的功能妖滔,而是提升了組件的重用性隧哮。
二、ApplicationContext
也許大家從零零星星的文章中了解到座舍,ApplicationContext是一個(gè)高級(jí)的BeanFactory沮翔,它在BeanFactory的基礎(chǔ)上擴(kuò)展了很多功能。至于它有哪些功能曲秉,我們接下來(lái)細(xì)細(xì)道來(lái)采蚀。
從ApplicationContext的類圖中可以看到,它除了簡(jiǎn)接地繼承了BeanFactory之外承二,還繼承了MessageSource榆鼠、ResourceLoader、ApplicationEventPublisher和EnvironmentCapable接口亥鸠,言外之意是它包含了這幾個(gè)接口定義的所有功能妆够。
2.1 MessageSource
MessageSource是一個(gè)解析消息的策略接口识啦,它支持參數(shù)化與國(guó)際化。也許大家并不是很理解這句話神妹,下面我們一步步地解析這句話的含義颓哮。
/**
* 用于解析消息的策略接口,支持此類消息的參數(shù)化和國(guó)際化
*/
public interface MessageSource {
/**
* 解析消息鸵荠,如果沒(méi)找到code對(duì)應(yīng)的消息就返回defaultMessage
*/
@Nullable
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
/**
* 解析消息题翻,如果沒(méi)找到code對(duì)應(yīng)的消息就拋異常
*/
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
/**
* 使用MessageSourceResolvable中的所有屬性解析消息
* MessageSourceResolvable用code[]是啥意思呢?腰鬼??
*/
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
從MessageSource的源碼可以看到塑荒,它定義了三個(gè)getMessage方法熄赡,根據(jù)Locale指定的地區(qū)解析code對(duì)應(yīng)的消息,并用args參數(shù)替換消息中的占位符齿税,最終返回解析后的消息彼硫。
為了幫助大家理解,我舉個(gè)簡(jiǎn)單的例子:
- 在Spring配置文件spring.xml中定義properties文件的前綴
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages</value>
</list>
</property>
</bean>
- 新建幾個(gè)以messages為前綴的properties文件凌箕,內(nèi)容和文件名如下:
message=我只是個(gè){0} #messages_zh_CN.properties
message=I am just a {0} #messages_en.properties
- 解析并獲取解析后的消息
ApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
String message1=context.getMessage("message", new String[]{"小孩"}, Locale.CHINA);
String message2=context.getMessage("message", new String[]{"child"}, Locale.ENGLISH);
System.out.println("message1=" + message1);
System.out.println("message2=" + message2);
- 打印輸出
message1=我只是個(gè)小孩
message2=I am just a child
通過(guò)這個(gè)例子拧篮,大家應(yīng)該理解參數(shù)化和國(guó)際化的意思了吧~~
我們繼續(xù)回到ApplicationContext,它繼承了MessageSource接口牵舱,說(shuō)明它具有解析參數(shù)化和國(guó)際化消息的功能串绩。
2.2 BeanFactory
要實(shí)現(xiàn)IOC容器,BeanFactory是不可或缺的一環(huán)芜壁,它負(fù)責(zé)bean的創(chuàng)建和管理礁凡。從ApplicationContext的類圖中可以看出,ApplicationContext并不是直接繼承BeanFactory接口慧妄,而是繼承了BeanFactory的子接口:ListableBeanFactory和HierarchicalBeanFactory顷牌。
2.2.1 BeanFactory
/**
* 創(chuàng)建、獲取bean以及一些bean相關(guān)的其他操作
*/
public interface BeanFactory {
/**
* FactoryBean的前綴
*/
String FACTORY_BEAN_PREFIX = "&";
/**
* 返回name對(duì)應(yīng)的bean對(duì)象
*/
Object getBean(String name) throws BeansException;
/**
* 返回指定type和name的bean對(duì)象
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
/**
* 返回現(xiàn)有對(duì)象或者用傳入的args創(chuàng)建對(duì)象塞淹,非原型模式傳入args會(huì)報(bào)錯(cuò)
*/
Object getBean(String name, Object... args) throws BeansException;
/**
* 只能返回指定類型的唯一對(duì)象窟蓝,大于小于一個(gè)都報(bào)錯(cuò)
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**
* 返回requiredType對(duì)應(yīng)的bean對(duì)象或者用傳入的args創(chuàng)建對(duì)象,非原型模式傳入args會(huì)報(bào)錯(cuò)
*/
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
/**
* 獲取requiredType類型的ObjectProvider
*/
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
/**
* 獲取requiredType類型的ObjectProvider
*/
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
/**
* 是否存在名字為name的bean
*/
boolean containsBean(String name);
/**
* 名字為name的bean是否單例bean
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* 是否原型bean
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* 指定bean是否該類型
*/
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
/**
* 指定bean是否該類型
*/
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
/**
* 獲取指定bean的類型
*/
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
/**
* 獲取指定bean的別名
*/
String[] getAliases(String name);
}
從BeanFactory的源碼中可以看出饱普,BeanFactory定義了一些對(duì)bean的基本操作运挫。繼承BeanFactory,ApplicationContext就有了對(duì)bean進(jìn)行操作的行為费彼。
2.2.2 ListableBeanFactory
上面BeanFactory定義的都是對(duì)單個(gè)bean進(jìn)行的操作滑臊,而ListableBeanFactory定義的操作大多數(shù)都是返回多個(gè)bean或者bean相關(guān)元素。從“Listable”就可以看出來(lái)箍铲,它定義了一些對(duì)bean進(jìn)行列表化的操作雇卷。因此,ApplicationContext也可以對(duì)bean進(jìn)行一些列表化操作。
/**
* 可以枚舉所有bean實(shí)例关划,而不是按客戶端的請(qǐng)求逐個(gè)嘗試按名稱查找bean
*/
public interface ListableBeanFactory extends BeanFactory {
/**
* 是否存在指定name的BeanDefinition
*/
boolean containsBeanDefinition(String beanName);
/**
* 獲取BeanFactory中BeanDefinition的數(shù)量
*/
int getBeanDefinitionCount();
/**
* 獲取所有BeanDefinition的名字
*/
String[] getBeanDefinitionNames();
/**
* 獲取指定類型的beanName數(shù)組
*/
String[] getBeanNamesForType(ResolvableType type);
/**
* 獲取指定類型的beanName數(shù)組
*/
String[] getBeanNamesForType(@Nullable Class<?> type);
/**
* 獲取指定類型的beanName數(shù)組小染,加了是否包含單例和非懶加載這幾個(gè)限制
*/
String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
/**
* 獲取指定類型的所有bean
*/
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
/**
* 獲取指定類型的所有bean,加了是否包含單例和非懶加載這幾個(gè)限制
*/
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException;
/**
* 獲取指定注解類型的beanNames
*/
String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
/**
* 獲取指定類型的bean對(duì)象
*/
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
/**
* 根據(jù)beanName和指定注解類型獲取注解bean對(duì)象
*/
@Nullable
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException;
}
2.2.3 HierarchicalBeanFactory
HierarchicalBeanFactory定義的方法相對(duì)少點(diǎn)贮折,它定義了對(duì)BeanFactory分級(jí)的一些操作裤翩,比如返回父BeanFactory。
/**
* 定義了對(duì)BeanFactory層次結(jié)構(gòu)的操作
*/
public interface HierarchicalBeanFactory extends BeanFactory {
/**
* Return the parent bean factory, or {@code null} if there is none.
*/
@Nullable
BeanFactory getParentBeanFactory();
/**
* 當(dāng)前context(不包含祖先context)是否含有指定name的bean
*/
boolean containsLocalBean(String name);
}
2.3 ResourceLoader
ResourceLoader是一個(gè)資源加載的策略接口调榄,繼承這個(gè)接口說(shuō)明ApplicationContext有資源加載的功能踊赠,這也是Spring容器的第一步操作——加載配置文件。因?yàn)榕渲梦募x了bean以及bean之間的關(guān)系每庆,所以只有把配置文件加載進(jìn)來(lái)才能創(chuàng)建筐带、管理bean。
/**
* 資源加載的策略接口
*/
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:". */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/**
* 返回指定路徑的資源缤灵,這里只返回一個(gè)伦籍,說(shuō)明不支持模式匹配
*/
Resource getResource(String location);
/**
* 返回統(tǒng)一的ClassLoader,而不是依賴于線程上下文ClassLoader
*/
@Nullable
ClassLoader getClassLoader();
}
2.3.1 ResourcePatternResolver
從名字就可以看出來(lái)腮出,ResourcePatternResolver對(duì)ResourceLoader進(jìn)行了擴(kuò)展帖鸦,它支持解析模式匹配的路徑。
/**
* 解析模式匹配的路徑胚嘲,加載資源
*/
public interface ResourcePatternResolver extends ResourceLoader {
/**
* classpath url的前綴
*/
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
/**
* 解析模式匹配的路徑作儿,返回多個(gè)Resource
*/
Resource[] getResources(String locationPattern) throws IOException;
}
2.4 ApplicationEventPublisher
ApplicationEventPublisher定義了事件發(fā)布的功能,可以將事件發(fā)布給注冊(cè)了此應(yīng)用所有匹配的監(jiān)聽(tīng)器慢逾。由此可見(jiàn)立倍,ApplicationContet也可以發(fā)布事件。
/**
* 定義了事件發(fā)布功能
*/
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
/**
* 通知在此應(yīng)用程序中注冊(cè)的所有匹配的listeners
*/
void publishEvent(Object event);
}
2.5 EnvironmentCapable
EnvironmentCapable中只定義了getEnvironment的方法侣滩,向外界暴露了Environment接口口注。Environment是Spring運(yùn)行時(shí)的環(huán)境,它包含了profiles和properties君珠。
在一個(gè)軟件的開(kāi)發(fā)過(guò)程中寝志,我們往往要經(jīng)過(guò)很多步驟,而這些執(zhí)行步驟往往要將項(xiàng)目部署在不同的環(huán)境策添,比如測(cè)試時(shí)部署在測(cè)試環(huán)境材部、上線時(shí)部署在生產(chǎn)環(huán)境。profile正是起到了區(qū)分環(huán)境的作用唯竹,容器只會(huì)加載當(dāng)前active的profile環(huán)境所定義的bean及其他配置乐导。
properties是指當(dāng)前應(yīng)用的屬性,它可以在System環(huán)境變量浸颓、properties文件等多個(gè)地方進(jìn)行配置物臂。
三旺拉、小結(jié)
ApplicationContext是一個(gè)BeanFactory,它包含了創(chuàng)建bean以及其他管理bean的功能棵磷,除此之外它還有下列功能:
- 解析參數(shù)化和國(guó)際化消息蛾狗;
- 資源加載;
- 事件發(fā)布仪媒;
- 暴露環(huán)境沉桌。