狐言不胡言

导航

ApplicationContext源码及其IOC容器初始化过程的简单介绍!

回顾一下IOC容器

IOC容器,就是为用户提供创建、管理、获取它们的类实例的容器,让用户在需要使用类对象的时候,只需要向IOC容器要就可以了,进而能够达到与具体的类解耦,为其他的高级功能和特性提供基础。

IOC容器在被使用之前,需要完成以下工作:

image.png

使用Spring的三种方式

  1. XML配置的方式
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="girl" class="di.MagicGirl"
     init-method="start" destroy-method="end">
        <constructor-arg type="java.lang.String" value="girl"></constructor-arg>
        <property name="friend" ref="boy"></property>
    </bean>

    <bean id="boy" class="di.Lad">
        <constructor-arg type="java.lang.String" value="boy"></constructor-arg>
        <constructor-arg type="di.MagicGirl" value="girl"></constructor-arg>
    </bean>
</beans>

通过XML的方式来指定扫描包的范围:

<context:component-scan base-package="demo.beans" />

通过ApplicationContext来使用:

 ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
  1. 注解配置的方式
@Component(initMethodName = "init", destroyMethodName = "destroy")
public class Lad implements Boy {

    @Autowired
    private Girl friend;
    
    public Lad(String name) {
        this.name = name;
    }
    
    @Autowired
    public Lad(@Value("tony") String name, @Qualifier("flower") Girl gf) {
        this.name = name;
        this.friend = gf;
        System.out.println("调用了含有Girl参数的构造方法");
    }
}

通过注解的方式指定扫描包的范围:

@Configuration
@ComponentScan(basePackages = "demo.beans")
public class AppConfig {
}

或者使用下面代码的方式:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
context.scan("demo.beans");
context.refresh();
  1. JavaBean配置的方式
@Configuration
@ComponentScan(basePackages = "demo.beans")
public class AppConfig {

    @Bean
    public Lad lad() {
        return new Lad("");
    }
}

通过上面简单的介绍,可以知道,当要使用Spring的时候,只需要使用Spring提供的ApplicationContext的API,而ApplicationContext就是IOC容器

ApplicationContext的继承体系

首先在看源码之前需要清楚ApplicationContext是什么,能够做些什么事情,怎么去使用它,以及它的实现类有哪些,有什么区别,初始化的过程又是什么样的?

ApplicationContext就是IOC容器,是提供IOC容器应用配置的普通接口。

下面看下ApplicationContext这个接口:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
}

下面是ApplicationContext这个接口提供的方法:

image.png

每个方法具体的作用如下:

方法名 作用
getId() IOC容器唯一的id
getApplicationName() 返回这个应用的名称
getDisplayName() 展示名,说明用的,让别人比较容易理解
getStartupDate() 容器启动的时间
getParent() 获得父容器,类似于父子工厂
getAutowireCapableBeanFactory() 获得自动装配的Bean工厂

ApplicationContext的父级接口

从上面的源码可以看到,ApplicationContext的父级接口也有很多:

image.png

下面是每个父级接口的作用:

父接口 作用
EnvironmentCapable 具有获取环境相关参数的功能,propertiess文件
ListableBeanFactory 提供Bean迭代功能的BeanFactory
HierarchicalBeanFactory 提供父容器的访问功能的BeanFactory
MessageSource 对国际化文件支持的基础接口
ApplicationEventPublisher 应用事件的发布接口
ResourcePatternResolver 资源解析和加载的接口

下面是UML类图:

image.png

Environment接口,是用来表示整个应用在运行时的环境,为了更加形象的理解Environment接口,可以把Spring的运行环境想象成两部分,一部分是Spring应用本身,另一部分就是Spring应用所处的环境。

HierarchicalBeanFactory接口,可以获得父工厂,只能子获取父,containsLocalBean方法是用来判断本工厂是否包含某个Bean的:

public interface HierarchicalBeanFactory extends BeanFactory {

	/**
	 * Return the parent bean factory, or {@code null} if there is none.
	 */
	@Nullable
	BeanFactory getParentBeanFactory();

	/**
	 * Return whether the local bean factory contains a bean of the given name,
	 * ignoring beans defined in ancestor contexts.
	 * <p>This is an alternative to {@code containsBean}, ignoring a bean
	 * of the given name from an ancestor bean factory.
	 * @param name the name of the bean to query
	 * @return whether a bean with the given name is defined in the local factory
	 * @see BeanFactory#containsBean
	 */
	boolean containsLocalBean(String name);

}

ApplicationContext的实现体系

image.png

从上图可以看到,从AbstractApplicationContext之后分成了两个部分,一部分是XML的实现,另一部分是通用的实现。

  1. ConfigurableApplicationContext接口
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
   ...
}

image.png

从上图可以看到,ConfigurableApplicationContext接口里面提供了很多的set方法,而ApplicationContext接口中只有get方法,所以可以知道,ConfigurableApplicationContext接口是可以配置的ApplicationContext,提供了父容器、环境、BeanFactory后置处理器、监听器等配置方法。

而且还提供了获取ConfigurableListableBeanFactory的方法,并且实现了Lifecycle, Closeable接口,这意味着它能够进行生命周期的控制和关闭的操作。

  1. AbstractApplicationContext抽象实现类
public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
   ...
}

AbstractApplicationContext继承了DefaultResourceLoader,默认为classpath资源加载器

查看源码可以看到,这个类实现了很多的通用方法,包括ApplicationContext、ConfigurableApplicationContext、BeanFactory、ListableBeanFactory、HierarchicalBeanFactory、MessageSource、ResourcePatternResolver、Lifecycle 8个父接口的方法实现,主要的目的就是为子类的实现进行了大量的减负,主要遗留了refreshBeanFactory、closeBeanFactory、getBeanFactory方法,这三个是具体的子类来实现的

	//---------------------------------------------------------------------
	// Abstract methods that must be implemented by subclasses
	//---------------------------------------------------------------------

	protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

	protected abstract void closeBeanFactory();

	@Override
	public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

从AbstractApplicationContext之后就出现了两个分支:AbstractRefreshableApplicationContext、GenericApplicationContext

  1. AbstractRefreshableApplicationContext类
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 1.1.3
 * @see #loadBeanDefinitions
 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory
 * @see org.springframework.web.context.support.AbstractRefreshableWebApplicationContext
 * @see AbstractXmlApplicationContext
 * @see ClassPathXmlApplicationContext
 * @see FileSystemXmlApplicationContext
 * @see org.springframework.context.annotation.AnnotationConfigApplicationContext
 */
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
}

image.png

自1.1.3版本提供的抽象实现,提供了Bean工厂刷新方法的实现,可以配置是否可以重写Bean定义以及循环依赖

  1. AbstractRefreshableConfigApplicationContext类
public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
		implements BeanNameAware, InitializingBean {
}

image.png

AbstractRefreshableConfigApplicationContext则增加了资源配置入口,通过Environment解析出配置的真实资源路径字符串。

  1. AbstractXmlApplicationContext
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
}

AbstractXmlApplicationContext里面主要实现了一些Bean定义的加载

ClassPathXmlApplicationContext、FileSystemXmlApplicationContext是xml资源加载方式的具体实现

  1. GenericApplicationContext
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    private final DefaultListableBeanFactory beanFactory;
}

它实现了BeanDefinitionRegistry接口,该接口定义了bean定义信息的注册行为。即我们可
以直接往GenericApplicationContext中注册bean定义

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
                throws BeanDefinitionStoreException {

        this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}

相关实现有:

AnnotationConfigApplicationContext 注解配置

GenericGroovyApplicationContext groovy方式配置

GenericXmlApplicationContext 通用的xml配置

StaticApplicationContext 静态资源配置方式

从源码可以看出AbstractRefreshableApplicationContext是继承式逐步扩展,GenericApplicationContext则是组合式扩展

上述类使用的例子:

@Configuration
@ComponentScan("edu.dongnao.courseware")
public class Application {
    
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        Lad cs = context.getBean("swk", Lad.class);
        cs.sayLove();
        
        ApplicationContext context1 = new FileSystemXmlApplicationContext("F:/workspace/idea/vip/spring-source/src/main/resources/application.xml");
        cs = context1.getBean("lad", Lad.class);
        cs.sayLove();

        context1 = new GenericXmlApplicationContext("file:F:/workspace/idea/vip/spring-source/src/main/resources/application.xml");
        cs = context1.getBean("swk", Lad.class);
        cs.sayLove();

        // 注解的方式
        ApplicationContext context2 = new AnnotationConfigApplicationContext(Application.class);
        Lad cs2 = context2.getBean(Lad.class);
        cs2.sayLove();

        System.out.println("------------------------------------------------------");
        GenericApplicationContext context3 = new GenericApplicationContext();
        new XmlBeanDefinitionReader(context3).loadBeanDefinitions("classpath:application.xml");
        new ClassPathBeanDefinitionScanner(context3).scan("edu.dongnao.courseware");
        // 一定要刷新
        context3.refresh();
        cs2 = context3.getBean("swk", Lad.class);
        cs2.sayLove();
        MagicGril ab = context3.getBean(MagicGril.class);
        ab.start();
        
        //  用法跟GenericApplicationContext一样
        // ApplicationContext context4 = new StaticApplicationContext();
    }
}

ApplicationContext的初始化过程

以ClassPathXmlApplicationContext为例:

跟踪代码路径:

image.png

refresh方法是最主要的部分,里面的每一个方法都是很重要的步骤:

@Override
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
                        //为刷新做准备,初始化各种状态,启动时间、关闭激活状态、配置文件信息等
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
                        //通知子类刷新内部的BeanFactory,并且返回刷新后的BeanFactory
                        //可以关注下refreshBeanFactory方法,两个子类对它重写的差异
                        // AbstractRefreshableApplicationContext#refreshBeanFactory中重新构建,完成 BeanDefinition的解析构建 // GenericApplicationContext#refreshBeanFactory则是设置ID,并且只能刷新一次
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
                        //给容器准备BeanFactory,并且配置相关的属性
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
                                //允许子类处理后置BeanFactory
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
                                //调用注册了BeanFactory后置处理器接口实例Bean对应的方法
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
                                //注册bean后置处理器接口的实例bean
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
                                //初始化国际化资源
				initMessageSource();

				// Initialize event multicaster for this context.
                                //初始化事件传播者
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
                                //Refresh事件,初始化一些指定的bean
				onRefresh();

				// Check for listener beans and register them.
                                //注册监听器bean
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
                                //完成bean工厂的初始化,初始化所有非懒加载的单例bean
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
                                //最后,发布finishRefresh事件
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
                                //销毁了所有已经创建了的单例bean
				destroyBeans();

				// Reset 'active' flag.
                                //重置active标记
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
}

本篇内容是对ApplicationContext的源码做一些简单的了解,以及对初始化过程的了解,后续会进行源码的分析。

posted on 2021-07-06 16:55  狐言不胡言  阅读(131)  评论(0编辑  收藏  举报