从UML关系上看,ApplicationContext与BeanFactory既有继承关系、又有关联关系
在BeanFactory篇中说过,很多ApplicationContext的实现类中,都引用了一个DefaultListableBeanFactory对象,并将bean管理操作都委托给它。以下是从GenericApplicationContext及其父类中摘录的一段代码,主要是为了让你感受下它与DefaultListableBeanFactory的关系
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
private final DefaultListableBeanFactory beanFactory;
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
public GenericApplicationContext(DefaultListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.beanFactory = beanFactory;
}
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
return this.beanFactory.getBeanDefinition(beanName);
}
//来自父类
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
//来自父类
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBeansOfType(type);
}
//来自父类
public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
assertBeanFactoryActive();
return getBeanFactory().isSingleton(name);
}
}
所以可以说ApplicationContext建立在BeanFactory的基础上,此外它还提供了很多面向应用开发的功能。从服务对象的角度理解,BeanFactory服务于Spring内部,ApplicationContext服务于开发者,几乎所有应用场合我们都会直接使用ApplicationContext,而非BeanFactory
我们把ApplicationContext的祖先类中有关BeanFactory的部分去掉,剩下的就是它提供的企业级功能特性
EnvironmentCapable:只有一个getEnvironment()方法,返回一个Environment对象,Environment内容比较多,后面会开单章,这里简要介绍下,Environment集成了两个关键特性,profiles和配置管理(其实本质上都是配置管理)
profile用于决定一个bean definition是否会被注册到spring容器,我们可以通过配置为项目指定一个或一组active profile,在通过xml或者注解定义一个bean时,可以指定一个或一组profile,只有指定的profile为active profile时,bean definition才会被注册(带profile的配置文件也是一样道理)
对应用来说,配置无疑是很重要的,包括properties文件、jvm参数、系统环境变量、servlet上下文参数等等,都属于Envirenment配置管理的范畴。Envirenment通过集成PropertySources组件,来进行配置管理,PropertySources维护一组PropertySource,系统环境变量、jvm参数、用户自定义的每个properties文件,最终都对应一个PropertySource对象,所有配置的读取操作最终都会委托它来完成
ApplicationEventPublisher:提供事件发布的能力,包括spring内部事件及用户自定义事件。spring在容器启停的一些关键阶段,会发布相应事件,以便于应用程序监听到事件(结合ApplicationListener一起使用)并做相应处理,比如容器启动的最终阶段会发布ContextRefreshedEvent、容器停止时会发布ContextClosedEvent,很多应用会监听它们,做一些初始化、资源释放、优雅关闭工作。此外,通过这套机制自定义事件发布、监听也是非常简单的,相当于帮你省去了实现观察者模式的工作量
ResourcePatternResolver:在Resource一文中已经介绍过,提供了资源加载能力(加载单个资源、或以通配符的方式加载所有满足条件的资源)
MessageSource:提供i18n风格消息访问的能力
扫描二维码关注公众号,回复: 13465351 查看本文章
再来看看ApplicationContext的子孙类们
ConfigurableApplicationContext:提供了配置ApplicationContext的能力,便于用户扩展,比如addBeanFactoryPostProcessor、addApplicationListener、setApplicationStartup、setParent,这些方法见名知意,就不多解释了
AbstractApplicationContext:作为ApplicationContext的抽象实现,用模板方法模式实现了很多通用逻辑,并且预留了很多方法允许子类覆盖。包括:
- 从beanFactory体系继承来的所有方法(统统委托给子类持有的BeanFactory引用实现,这个引用通常是DefaultListableBeanFactory),
- 上述的企业级特性对应的方法,如publishEvent(通过ApplicationEventMulticaster以广播的形式发布事件)、getResources(使用PathMatchingResourcePatternResolver,支持加载满足通配符的任意个资源)
- 容器启动方法refresh,非常关键,包含的内容很多,后续会开单章说明
AbstractApplicationContext子类分为两个派系,既是否refreshable,refreshable表示支持重复调用refresh(容器启动)方法,每次refresh都会优雅销毁老的BeanFactory,然后用一个全新的BeanFactory作为替代
//摘录自AbstractRefreshableApplicationContext @Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
//摘录自GenericApplicationContext @Override protected final void refreshBeanFactory() throws IllegalStateException { //通过状态位控制只允许refresh一次 if (!this.refreshed.compareAndSet(false, true)) { throw new IllegalStateException( "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once"); } this.beanFactory.setSerializationId(getId()); }
抛开是否refreshable来说,GenericXmlApplicationContext、FileSystemXmlApplicationContext、ClassPathXmlApplicationContext这三个实现比较类似,都使用XmlBeanDefinitionReader从xml文件加载beanDefinition,FileSystemXmlApplicationContext、ClassPathXmlApplicationContext的区别在于分别使用FileSystemResource、ClassPathResource表示xml资源,而GenericXmlApplicationContext则更灵活,你可以直接通过Resource来构造它、也可以通过xml路径来构造,不论xml来源何处,只要它可以被加载为Resource即可
public static void main(String[] args) throws IOException { ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("D:\\code\\spring\\spring-introduction\\src\\main\\resources\\bean.xml"); System.out.println("applicationContext1.getBean===>" + applicationContext1.getBean("user")); ApplicationContext applicationContext2 = new ClassPathXmlApplicationContext("bean.xml"); System.out.println("applicationContext2.getBean===>" + applicationContext2.getBean("user")); ApplicationContext applicationContext3 = new GenericXmlApplicationContext("classpath:bean.xml"); System.out.println("applicationContext3.getBean===>" + applicationContext3.getBean("user")); ApplicationContext applicationContext4 = new GenericXmlApplicationContext("file:D:\\code\\spring\\spring-introduction\\src\\main\\resources\\bean.xml"); System.out.println("applicationContext4.getBean===>" + applicationContext4.getBean("user")); Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:bean.xml"); //通过Resource构造GenericXmlApplicationContext ApplicationContext applicationContext5 = new GenericXmlApplicationContext(resources); System.out.println("applicationContext5.getBean===>" + applicationContext5.getBean("user")); }
##resources/demo.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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.example.spring.entity.User" > <property name="id" value="001"/> <property name="name" value="zhang san"/> </bean> </beans>
AnnotationConfigApplicationContext:使用component class(通常是注解了@Configuration的类,也支持@Component以及JSR-330标准中的@Inject注解)作为输入,来加载beanDefinition
@Configuration public class AnnotationApplicationContextDemo { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationApplicationContextDemo.class); System.out.println(applicationContext.getBean("userName")); } @Bean public String userName() { return "bobo"; } }
实际开发场景下,java config类通常不会只包含@Bean注解(尤其是项目的主配置类),往往还包含@ComponentScan、@PropertySource、@Import等注解,除了这些java config类上的注解外,还有诸如@Component、@Autowired、@Value、@Resource、@Inject、@PostConstruct、@PreDestroy、@EventListener等用于普通类的注解,针对这些注解的处理逻辑,是容器启动过程中的核心,搞清楚这些对我们理解IOC容器很有帮助
AnnotationConfigApplicationContext实例化时会创建并维护一个AnnotatedBeanDefinitionReader实例,后者在实例化时,会调用AnnotationConfigUtils#registerAnnotationConfigProcessors方法注册与各种注解处理相关的PostProcessor(包括BeanFactoryPostProcessor和BeanPostProcessor),上述注解的处理逻辑就由这些PostProcessor负责,比如ConfigurationClassPostProcessor负责@Bean、@ComponentScan、@PropertySource、@Import这些与java config类相关的注解,AutowiredAnnotationBeanPostProcessor负责@Autowired、@Value、@Inject这些依赖注入相关的注解