IOC
三种依赖注入的方式
构造器注入

setter方法注入

外界(IOC)就可以通过调用 setNewsListener 和 setNewPersistener 方法为 FXNewsProvider 对象注入所依赖的对象了。
setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些,可以在对象构造完成后再注入。这就好比你可以到酒吧坐下后再决定要点什么啤酒,可以要百威,也可以要大雪,随意性比较强。如果你不急着喝,这种方式当然是最适合你的。
接口注入

FXNewsProvider 为了让IoC Service Provider为其注入所依赖的 IFXNewsListener ,首先需要实现
IFXNewsListenerCallable 接口,这个接口会声明一个 injectNewsListner 方法(方法名随意),
该方法的参数(很重要),就是所依赖对象的类型。这样, InjectionServiceContainer 对象,即对应的IoC
Service Provider就可以通过这个接口方法将依赖对象注入到被注入对象 FXNewsProvider 当中
三种方式对比

IOC的可重用性
当被注入的对象又多了一个依赖对象时,如果没用ioc的时候,我们需要重写一个类,把被注入的对象和依赖对象绑定起来,用了ioc之后,不需要重写一个类,注入的时候把需要的依赖对象传进来即可。
IoC Service Provider
抽象出来的概念,指代任何将IoC场景中的业务对象绑定到一起的实现方式,可以是代码,容器之类的,只要是把对象进行绑定就是Ioc Service Provider
eg:(这就是其中一种,强耦合的情况)
Ioc Service Provider的职责
业务对象的构建管理
业务对象无需关心所依赖的对象如何构建如何取得,但这部分工作始终需要有人来做。所以,IoC Service Provider需要将对象的构建逻辑从客户端对象(就是对象A要引用B,那么A就是客户端对象)那里剥离出来,以免这部分逻辑污染业务对象的实现
业务对象间的依赖绑定
对于IoC Service Provider来说,这个职责是最艰巨也是最重要的,这是它的最终使命之所在。如果不能完成这个职责,那么,无论业务对象如何的“呼喊”,也不会得到依赖对象的任何响应(最常见的倒是会收到一个 NullPointerException )。IoC Service Provider通过结合之前构建和管理的所有业务对象,以及各个业务对象间可以识别的依赖关系,将这些对象所依赖的对象注入绑定,从而保证每个业务对象在使用的时候,可以处于就绪状态。
Ioc Service Provider如何管理对象间的依赖关系
直接编码方式
通过程序编码的方式将被注入对象和依赖对象注册到容器中,并明确它们相互之间的依赖注入关系
构造器注入、setter方法注入
通过为相应的类指定对应的具体实例,可以告知IoC容器,当我们要这种类型的对象实例时,请将容器中注册的、对应的那个具体实例返回给我们IoContainer container = ...;container.register(FXNewsProvider.class,new FXNewsProvider());container.register(IFXNewsListener.class,new DowJonesNewsListener());...FXNewsProvider newsProvider = (FXNewsProvider)container.get(FXNewsProvider.class);newProvider.getAndPersistNews();
接口注入
IoContainer container = ...;container.register(FXNewsProvider.class,new FXNewsProvider());container.register(IFXNewsListener.class,new DowJonesNewsListener());...//通过bind方法将被注入对象所依赖的对象绑定到容器中,会根据这个绑定信息,//将IFXNewsListener 注册到容器中的对象实例注入到“被注入对象”——FXNewsProvider 中//并最终返回已经组装完毕的 FXNewsProvider 对象。container.bind(IFXNewsListenerCallable.class, container.get(IFXNewsListener.class));...FXNewsProvider newsProvider = (FXNewsProvider)container.get(FXNewsProvider.class);newProvider.getAndPersistNews();
配置文件方式
<bean id="newsProvider" class="..FXNewsProvider"><property name="newsListener"><ref bean="djNewsListener"/></property><property name="newPersistener"><ref bean="djNewsPersister"/></property></bean><bean id="djNewsListener"class="..impl.DowJonesNewsListener"></bean><bean id="djNewsPersister"class="..impl.DowJonesNewsPersister"></bean>
container.readConfigurationFiles(...);
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("newsProvider");
newsProvider.getAndPersistNews()
元数据方式(注解)
指明通过构造方法注入
module提供依赖关系
获取依赖信息并最终完成绑定
Injector injector = Guice.createInjector(new NewsBindingModule());
FXNewsProvider newsProvider = injector.getInstance(FXNewsProvider.class);
newsProvider.getAndPersistNews();
Spring的Ioc(Ioc Service Provider只是其中一个部分)
容器类型之BeanFactory(生产Bean的工厂)
基本介绍
基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景, BeanFactory 是比较合适的IoC容器选择。
定义
public interface BeanFactory {
// 从容器中根据beanname获取
Object getBean(String name) throws BeansException;
// 延迟加载对象
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
// 是否存在beanName
boolean containsBean(String name);
// 这个 beanName 是否是单例的. 映射成 bean
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// 是否多例.
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
// 类型是否匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
// 获取bean的类型
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
// 获取别名
String[] getAliases(String name);
}
BeanFactory的对象注册和依赖绑定
继承关系图(Factory一般使用DefaultListableBeanFactory)
底层原理
public static void main(String[] args)
{
//DefaultListableBeanFactory同时作为BeanDefinitionRegistry来注册对象信息
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
BeanFactory container = (BeanFactory)bindViaCode(beanRegistry);
FXNewsProvider newsProvider = ➥
(FXNewsProvider)container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
}
public static BeanFactory bindViaCode(BeanDefinitionRegistry registry)
{
//创建bean定义
AbstractBeanDefinition newsProvider = ➥
new RootBeanDefinition(FXNewsProvider.class,true);
AbstractBeanDefinition newsListener = ➥
new RootBeanDefinition(DowJonesNewsListener.class,true);
AbstractBeanDefinition newsPersister = ➥
new RootBeanDefinition(DowJonesNewsPersister.class,true);
// 将bean定义注册到容器中,后面需要某个对象就根据bean定义来创建
registry.registerBeanDefinition("djNewsProvider", newsProvider);
registry.registerBeanDefinition("djListener", newsListener);
registry.registerBeanDefinition("djPersister", newsPersister);
// 指定依赖关系
// 1. 可以通过构造方法注入方式
ConstructorArgumentValues argValues = new ConstructorArgumentValues();
argValues.addIndexedArgumentValue(0, newsListener);
argValues.addIndexedArgumentValue(1, newsPersister);
newsProvider.setConstructorArgumentValues(argValues);
// 2. 或者通过setter方法注入方式
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue(new PropertyValue("newsListener",newsListener));
propertyValues.addPropertyValue(new PropertyValue("newPersistener",newsPersister));
newsProvider.setPropertyValues(propertyValues);
// 绑定完成
return (BeanFactory)registry;
}
基于xml文件的对象注册和依赖绑定
xml文件包含bean定义和依赖关系
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" ➥
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="djNewsProvider" class="..FXNewsProvider">
<constructor-arg index="0">
<ref bean="djNewsListener"/>
</constructor-arg>
<constructor-arg index="1">
<ref bean="djNewsPersister"/>
</constructor-arg>
</bean>
<bean id="djNewsListener" class="..impl.DowJonesNewsListener">
</bean>
<bean id="djNewsPersister" class="..impl.DowJonesNewsPersister">
</bean>
</beans>
加载xml文件得到BeanFactory
public static void main(String[] args)
{
//DefaultListableBeanFactory同时作为BeanDefinitionRegistry来注册对象信息
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
BeanFactory container = (BeanFactory)bindViaXMLFile(beanRegistry);
FXNewsProvider newsProvider = ➥
(FXNewsProvider)container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
}
public static BeanFactory bindViaXMLFile(BeanDefinitionRegistry registry)
{
//把DefaultListableBeanFactory放入reader中
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
//通过reder加载xml文件并注册bean定义和依赖绑定
reader.loadBeanDefinitions("classpath:../news-config.xml");
return (BeanFactory)registry;
// 或者直接
//return new XmlBeanFactory(new ClassPathResource("../news-config.xml"));
//因为XmlBeanFactory是DefaultListableBeanFactory的子类,可直接根据xml文件注册bean
//和依赖绑定,底层还是通过reder去加载的,如下图所示
}
XmlBeanFactory的注册对象和依赖绑定:


基于注解的对象注册和依赖绑定(使用使用classpath-scanning)
@Component //把Component标志的类加入到容器管理
public class FXNewsProvider
{
@Autowired //告诉容器要为当前对象注入Autowired标志的对象
private IFXNewsListener newsListener;
@Autowired
private IFXNewsPersister newPersistener;
public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister)
{
this.newsListener = newsListner;
this.newPersistener = newsPersister;
}
...
}
@Component
public class DowJonesNewsListener implements IFXNewsListener
{
...
}
@Component
public class DowJonesNewsPersister implements IFXNewsPersister
{
...
}
启动注解扫描功能
读取配置文件,然后发现有component-scan 去扫描注解注册对象和依赖绑定
public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("配置文件路径");
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("FXNewsProvider");
newsProvider.getAndPersistNews();
}
自动绑定和手动绑定的优缺点
对象的scope(存活时间)
singleton

prototype
容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继生命周期的管理工作,包括该对象的销毁。也就是说,容器每次返回给请求方一个新的对象实例之后,就任由这个对象实例“自生自灭”了。
request
request通常的配置形式如下:
Spring容器,即 XmlWebApplicationContext 会为每个HTTP请求创建一个全新的 Request-Processor 对象供当前请求使用,当请求结束后,该对象实例的生命周期即告结束。当同时有10个HTTP请求进来的时候,容器会分别针对这10个请求返回10个全新的 RequestProcessor 对象实例,且它们之间互不干扰。从不是很严格的意义上说,request可以看作prototype的一种特例,除了场景更加具体之外,语意上差不多。
session
对于Web应用来说,放到session中的最普遍的信息就是用户的登录信息,对于这种放到session中
的信息,我们可使用如下形式指定其scope为session:
Spring容器会为每个独立的session创建属于它们自己的全新的 UserPreferences 对象实例
global session
还是 userPreferences ,不过scope对应的值换一下,如下所示:
global session只有应用在基于portlet的Web应用程序中才有意义,它映射到portlet的global范围的
session。如果在普通的基于servlet的Web应用中使用了这个类型的scope,容器会将其作为普通的session
类型的scope对待。
自定义scope


BeanFactory注册自定义scope:
Scope threadScope = new ThreadScope();
beanFactory.registerScope("thread",threadScope);
CustomScopeConfigurer注册自定义scope(专门的,在xml文件配置即可)
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread" value="com.foo.ThreadScope"/>
</map>
</property>
</bean>
工厂方法与FactoryBean
场景分析
静态工厂方法
getInstance不需要参数
public class StaticBarInterfaceFactory
{
public static BarInterface getInstance()
{
return new BarInterfaceImpl();
}
}
<bean id="foo" class="...Foo">
<property name="barInterface">
<ref bean="bar"/>
</property>
</bean>
<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>
getInstance需要参数
public class StaticBarInterfaceFactory
{
public static BarInterface getInstance(Foobar foobar)
{
return new BarInterfaceImpl(foobar);
}
}
<bean id="foo" class="...Foo">
<property name="barInterface">
<ref bean="bar"/>
</property>
</bean>
<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance">
<constructor-arg>
<ref bean="foobar"/> //传入工厂方法的参数
</constructor-arg>
</bean>
<bean id="foobar" class="...FooBar"/>
非静态工厂方法
public class NonStaticBarInterfaceFactory
{
public BarInterface getInstance()
{
return new BarInterfaceImpl();
}
}
<bean id="foo" class="...Foo">
<property name="barInterface">
<ref bean="bar"/>
</property>
</bean>
<bean id="barFactory" class="...NonStaticBarInterfaceFactory"/>
//工厂是由factory-bean指定的 而不是class
<bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>
factoryBean
当某些对象的实例化过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器的时候,就可以实现 org.spring-framework.beans.factory.FactoryBean 接口,给出自己的对象实例化逻辑代码。
结构
public interface FactoryBean {
Object getObject() throws Exception; //生产对象,可以是自定义的对象
Class getObjectType();//仅返回 getObject() 方法所返回的对象的类型,如果预先
//无法确定,则返回 null
boolean isSingleton();//以singleton形式存在容器,则返回 true ,否则返回 false ;
}
使用

获取FactoryBean本身
可以通过在bean定义的 id 之前加前缀 & 来达到目的。代码清单4-34展示了获取 FactoryBean 本身与获取 FactoryBean “生产”的对象之间的差别。
Object nextDayDate = container.getBean("nextDayDate");
assertTrue(nextDayDate instanceof DateTime);
Object factoryBean = container.getBean("&nextDayDate");
assertTrue(factoryBean instanceof FactoryBean);
assertTrue(factoryBean instanceof NextDayDateFactoryBean);
Object factoryValue = ((FactoryBean)factoryBean).getObject();
assertTrue(factoryValue instanceof DateTime);
assertNotSame(nextDayDate, factoryValue);
assertEquals(((DateTime)nextDayDate).getDayOfYear(),((DateTime)factoryValue).getDayOfYear());
为什么scope为prototype得到的却是同一个实例
场景再现
解决办法之方法注入
得到所在的容器然后通过容器getBean
解决办法之 BeanFactoryAware得到bean所在的容器

解决办法之ObjectFactoryCreatingFactoryBean

方法替换(aop)
把getAndPersistNews()逻辑替换了,直接 在里面每次去找容器拿对象即可。
容器的底层
容器的作用

容器会以某种方式加载Configuration Metadata(通常也就是XML格式的配置信息),然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统,容器在实现上可分为1、容器启动阶段,2、Bean实例化阶段

容器启动阶段
基本介绍
首先会通过某种途径加载Configuration MetaData。除了代码方式比较直接,在大部分情况下,容器需要依赖某些工具类( BeanDefinitionReader )对加载的Configuration MetaData进行解析和分析,并将分析后的信息编组为相应的 BeanDefinition ,最后把这些保存了bean定义必要信息的 BeanDefinition ,相应的BeanDefinitionRegistry将信息注册到容器里 ,这样容器启动工作就完成了。
容器存储BeanDefinition 使用的是ConcurrentHashMap
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
xml的bean定义——》Beandeinition
总地来说,该阶段所做的工作可以认为是准备性的,重点更加侧重于对象管理信息的收集。当然,
一些验证性或者辅助性的工作也可以在这个阶段完成。
插手容器启动阶段(处理容器内所有符合条件的 BeanDefinition,在实例化阶段之前可以修改BeanDefinition—-BeanFactoryPostProcessor)
BeanFactory(需要手动装配BeanFactoryPostProcessor)
// 声明将被后处理的BeanFactory实例
ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("..."));
// 声明要使用的BeanFactoryPostProcessor
PropertyPlaceholderConfigurer propertyPostProcessor = new PropertyPlaceholderConfigurer();
propertyPostProcessor.setLocation(new ClassPathResource("..."));
// 执行后处理操作
propertyPostProcessor.postProcessBeanFactory(beanFactory);
ApplicationContext(可以识别配置文件里的BeanFactoryPostProcessor)
<beans>
//BeanFactoryPostProcessor
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>conf/jdbc.properties</value>
<value>conf/mail.properties</value>
</list>
</property>
</bean>
</beans>
了解几个BeanFactoryPostProcessor
PropertyPlaceholderConfigurer
允许我们在XML配置文件中使用占位符(PlaceHolder),并将这些占位符所代表的资源单独配置到简单的properties文件中来加载。以数据源的配置为例,使用了 PropertyPlaceholderConfigurer 之后 ```xml
//配置PropertyPlaceholderConfigurer //可以使用占位符conf/jdbc.properties conf/mail.properties ${jdbc.url} ${jdbc.driver} ${jdbc.username} ${jdbc.password} 100
properties文件内容<br />
**基本机制:当 BeanFactory 在第一阶段加载完成所有配置信息时, BeanFactory 中保存的对象的属性信息还只是以占位符的形式存在,如 ${jdbc.url} 、 ${jdbc.driver} 。当PropertyPlaceholderConfigurer 作为 BeanFactoryPostProcessor 被应用时,它会使用properties配置文件中的配置信息来替换相应 BeanDefinition 中占位符所表示的属性值。这样,当进入容器实现的第二阶段实例化bean时,bean定义中的属性值就是最终替换完成的了。**
PropertyPlaceholderConfigurer 不单会从其配置的properties文件中加载配置项,同时还会检查Java的 System 类中的Properties,可以通过 setSystemPropertiesMode() 或者 setSystemProper-tiesModeName() 来控制是否加载或者覆盖 System 相应Properties的行为。 PropertyPlaceholder-Configurer 提供了SYSTEM_PROPERTIES_MODE_FALLBACK 、 SYSTEM_PROPERTIES_MODE_NEVER 和 SYSTEM_<br />PROPERTIES_MODE_OVERRIDE 三种模式。**默认采用的是 SYSTEM_PROPERTIES_ MODE_FALLBACK ,即如果properties文件中找不到相应配置项,则到 System 的Properties中查找**,我们还可以选择不检查 System<br />的Properties或者覆盖它
2. PropertyOverrideConfigurer
可以在properties文件中对xml文件中配置的信息进行覆盖,<br />properties的使用规则:**beanName.propertyName=value**
```xml
<beans>
//配置PropertyPlaceholderConfigurer
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
//pool-adjustment.properties中没有提供的配置项将继续使用原来XML配置中的默认值。
<property name="location" value="pool-adjustment.properties"/>
</bean>
//可以使用占位符
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" ➥
destroy-method="close">
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="driverClassName">
<value>${jdbc.driver}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
//xml文件里的maxActive是100,等下我们可以在properties里覆盖
<property name="maxActive">
<value>100</value>
</property>
</bean>
</beans>
properties文件
等下maxActive就变成了50
PropertyOverrideConfigurer 的父类PropertyResourceConfigurer 提供了一个protected类型的方法 convertPropertyValue ,允许子类覆盖这个方法对相应的配置项进行转换,如对加密后的字符串解密之后再覆盖到相应的bean定义中,PropertyPlaceholderConfigurer也继承了PropertyResourceConfigurer 所以也有类似功能
- CustomEditorConfigurer
用处:我们从xml文件读出的内容都是字符串,但是我们要把他转换成一个完整的对象,先不管他的转换工作是谁做的,转换需要一些信息,比如String—>Integer之类的,CustomEditorConfig就是提供这种信息的。
转换工作的对象:PropertyEditor ,Spring内部通过JavaBean的 PropertyEditor 来帮助进行 String 类型到其他类型的转换工作。只要为 每 种 对 象 类 型 提 供 一 个 PropertyEditor , 就 可 以 根 据 该 对 象 类 型 取 得 与 其 相 对 应 的PropertyEditor 来做具体的类型转换
自定义 PropertyEditor:
2.0之后所提倡的注册自定义 PropertyEditor 的方式
Bean实例化阶段
基本介绍以及实例化过程
经过第一阶段,现在所有的bean定义信息都通过 BeanDefinition 的方式注册到了容器中。当某个请求方通过容器的 getBean 方法明确地请求某个对象,或者因依赖关系容器需要隐式地调用 getBean 方法时,就会触发第二阶段的活动。要经过getBean()才能启动第二阶段
该阶段,容器会首先检查所请求的对象之前是否已经初始化。如果没有,则会根据注册的BeanDefinition 所提供的信息实例化被请求对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当该对象装配完毕之后,容器会立即将其返回请求方使用。如果说第一阶段只是根据图纸装配生产线的话,那么第二阶段就是使用装配好的生产线来生产具体的产品了。
实例化过程:
BeanFactory和ApplicationContext实例化的区别
BeanFactory
对象实例化默认采用延迟初始化。通常情况下,当对象A被请求而需要第一次实例化的时候,如果它所依赖的对象B之前同样没有被实例化,那么容器会先实例化对象A所依赖的对象。这时容器内部就会首先实例化对象B,以及对象 A依赖的其他还没有被实例化的对象。这种情况是容器内部调用 getBean() ,对于本次请求的请求方是隐式的。
ApplicationContext
启动之后会实例化所有的bean定义,但 ApplicationContext 在实现的过程中依然遵循Spring容器实现流程的两个阶段,只不过它会在容器启动阶段的活动完成之后,紧接着调用注册到该容器的所有bean定义的getBean()方法 。这就是为什么当你得到 ApplicationContext 类型的容器引用时,容器内所有对象已经被全部实例化完成(refresh方法)。
Bean的实例化与BeanWrapper

Aware接口
BeanPostProcessor(前置处理)
特殊的BeanPostProcessor(不走流程图)

InitializingBean 和 init-method
DisposableBean 与 destroy-method

容器类型之ApplicationContext
基本介绍
ApplicationContext 在 BeanFactory 的基础上构建,是相对比较高
级的容器实现,除了拥有 BeanFactory 的所有支持, ApplicationContext 还提供了其他高级特性,比如事件发布、国际化信息支持等,这些会在后面详述。 ApplicationContext 所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于 BeanFactory 来说, ApplicationContext 要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之 BeanFactory 也会长一些。在那些系统资源充足,并且要求更多功能的场景中,ApplicationContext 类型的容器是比较合适的选择。
几个高级特性
BeanFactory和ApplicationContext的继承关系

几个常见的实现

高级特性之统一资源加载策略
场景分析
资源查找后返回的形式多种多样,没有一个统一的抽象。理想情况下,资源查找完成后,返回给客户端的应该是一个统一的资源抽象接口,客户端要对资源进行什么样的处理,应该由资源抽象接口来界定,而不应该成为资源的定位者和查找者同时要关心的事情,所以Spring提出了一套基于 org.springframework.core.io.Resource 和org.springframework.core.io.ResourceLoader 接口的资源抽象和加载策略。这两个不是ApplicationContext才能用的,是一直都有这两个,然后ApplicationContext间接继承了这两个所以就有相应的特性。
Resource
BeanFactory就使用过Resource

接口定义
几种常见的资源类型

ResourceLoader(查找和定位资源)
定义
可用的ResourceLoader
- DefaultResourceLoader(返回单个Resource实例)

FileSystemResourceLoader(继承DefaultResourceLoader,覆盖了getResourceByPath方法,返回单个resource实例)
- ResourcePatternResolver(返回多个resource实例)
Resource和ResourceLoader体系结构图

ApplicationContext的资源加载真相(委派给ResourcePatternResolver和DefaultResourceLoader)
ApplicationContext作为ResourcePatternResolver和DefaultResourceLoader的好处
- 扮演 ResourceLoader 的角色

- ResourceLoader 类型的注入

- Resource 类型的注入

高级特性之事件发布
自定义事件发布
自定义事件发布类型结构图

spring的事件发布类型图
使用spring的事件发布

多配制模块的简化
使用ApplicationContext加载多个文件
使用BeanFactory加载多个文件
ApplicationContext通过类来加载某些文件



