撸一撸Spring Framework-IoC-ApplicationContext

 从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这些依赖注入相关的注解

猜你喜欢

转载自blog.csdn.net/wb_snail/article/details/121199586