spring boot 加载过程分析(二)

在上一篇我们分析了SpringApplication的实例化过程,及环境配置相关的过程。现在接着看createApplicationContext方法。在分析ApplicationContext之前,先试想一下它是怎么把配置文件或注释bean解析成BeanDefinition的?又是怎么把BeanDefinition注册到容器的?怎么实现bean初始化及依赖注入的?带着这些问题我们往下看。

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			} catch (ClassNotFoundException ex) {
				throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
复制代码

首先是根据服务类型来确定最终加载并实例化的类,这里是Servlet类型,对应类为AnnotationConfigServletWebServerApplicationContext,注意在2版本里,该类不再是1版本中的AnnotationConfigEmbeddedWebApplicationContext了。其继承体系如下图(眼花缭乱...不着急慢慢来看)

这里留意一下这几个接口或类,先眼混个眼熟。

  • BeanDefinitionRegistry 接口
  • ResourceLoader 接口
  • BeanFactory 接口
  • ConfigurableApplicationContext 接口
  • AbstractApplicationContext 抽象类
  • GenericApplicationContext 实现类
  • GenericWebApplicationContext 实现类

1. AnnotationConfigServletWebServerApplicationContext

ok,下面接着看AnnotationConfigServletWebServerApplicationContext的实例化过程,默认构造方法如下代码.

public AnnotationConfigServletWebServerApplicationContext() {
	this.reader = new AnnotatedBeanDefinitionReader(this);
	this.scanner = new ClassPathBeanDefinitionScanner(this);
}
复制代码

这里又引入两个类,我们逐个来看。类实例化会根据加载机制,逐一实例化其父类,我们从当前类逐步往上分析

1.1 AnnotatedBeanDefinitionReader

从名字可以看出,这是一个beanDefinition的读取加载器,负责从注解类型解析注册bean信息到容器中。这里的BeanDefinitionRegistry就是提供容器bean信息注册的接口,通过上面的构造方法可以知道,传入的参数就是当前实例化对象。

private final BeanDefinitionRegistry registry;
// bean命名
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
// scope解析
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
// 构造器
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	Assert.notNull(environment, "Environment must not be null");
	this.registry = registry;
	this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
复制代码

首先初始化了两个辅助类,帮助解析读取bean的名字及部分属性

AnnotationBeanNameGenerator 生成beanName(value属性) AnnotationScopeMetadataResolver 解析@scope注解

然后调用工具类的registerAnnotationConfigProcessors来注册部分通用注解配置处理类,方法较长不再贴代码,简单介绍一下。它首先会为beanFactory增加默认的@Order, @Lazy, @Qualifier的实现处理类,再然后是注册一批RootBeanDefinition到容器中。

ConfigurationClassPostProcessor  处理@Configuration
AutowiredAnnotationBeanPostProcessor  处理@Autowired, @Inject
RequiredAnnotationBeanPostProcessor  处理@Required
CommonAnnotationBeanPostProcessor  支持JSR-250, @PostConstruct, @PreDestroy
PersistenceAnnotationBeanPostProcessor 支持jpa注解(如果有则配置)
EventListenerMethodProcessor  处理@EventListener的方法
复制代码

上面这些类有的实现BeanPostProcessor,有的实现ApplicationContextAware。至于具体实现这里就不再分析,感兴趣的可以自行深入了解。ok,到此AnnotatedBeanDefinitionReader初始化时做的事情分析完了,至于其核心方法register,后面用到再作分析。

1.2 ClassPathBeanDefinitionScanner

基本上功能和上面类一致,只是加载来源不同而已,在初始化的过程中,不上加载上面一堆处理器,中是增加了@Component的filters。当然其核心方法scan这里也不再细聊,等用到时再分析。

至此,AnnotationConfigServletWebServerApplicationContext自身初始化干了什么我们都知道了。而且也能猜到,上面两个类就是实现了BeanDefinition的解读,以便后面进行注册。接下来我们看其父类做了哪些工作。其中ServletWebServerApplicationContext,GenericWebApplicationContext这两个类可以直接忽略,着重了解GenericApplicationContext, AbstractApplicationContext, DefaultResourceLoader这三个类做了什么。

2. GenericApplicationContext

public GenericApplicationContext() {
    this.beanFactory = new DefaultListableBeanFactory();
}
复制代码

初始化了spring中默认的bean容器,这里先不细说,下面一章会仔细分析一下该类初始化具体做了什么及有哪些主要方法。

3. AbstractApplicationContext

AbstractApplicationContext也很简单,就是初始化了一个资源匹配类PathMatchingResourcePatternResolver。它是基于模式匹配的,默认使用AntPathMatcher进行路径匹配,它除了支持ResourceLoader支持的前缀外,还额外支持“classpath*:”用于加载所有匹配的类路径Resource,ResourceLoader不支持前缀“classpath*:”:

4. DefaultResourceLoader

默认资源加载类,维护一个classLoader对象,核心方法getResource,不再详述。

5. DefaultListableBeanFactory

关于beanFactory的具体功能作用等,我们到用到时再深入了解,并且网上相关资料也较多,这里就不再详述,暂时只介绍其初始化过程干了什么。之前我们分析了,在ApplicationContext初始化过程中,调用了beanFactory的默认实现DefaultListableBeanFactory,下面是其继承体系图,简单了解一下。

还记得之前让留意的两个接口BeanDefinitionRegistry,BeanFactory吧,这里就有他们的核心实现。ok,先看一下DefaultListableBeanFactory的部分源码。

/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

public DefaultListableBeanFactory() {
    super();
}
复制代码

暂且认为它啥都没干吧。这里有一个map,通过注释可以知道它是bean的一个映射。然后看他的父类AbstractAutowireCapableBeanFactory

public AbstractAutowireCapableBeanFactory() {
    super();
    ignoreDependencyInterface(BeanNameAware.class);
    ignoreDependencyInterface(BeanFactoryAware.class);
    ignoreDependencyInterface(BeanClassLoaderAware.class);	
}
复制代码

也很简单嘛,只是简单配置了几个依赖需求忽略的接口类。不过这里有一个成员变量需要了解一下。其实就是指定了一个bean实现化的策略实现,通过cglib来生成子类实现。

// bean实例化的策略实现类
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
复制代码

再往上查看,发现初始化没再做其他工作了,无外乎初始化一些Registry相关的实现类。对于这些类具体干嘛,到后面具体的应用时再分析。OK,通过这条线,我们知道了beanFactory已经具备了注册bean, 获取bean的功能。而之前也了解到applicationContext实现继承这些接口,变相的给applicationContext赋予了bean注册,bean实例化等功能,细节后面再聊。

至些,createApplicationContext方法干的事情基本结束了。可以知道,其实为后续bean的生命周期管理,各种解析配置等初始化了一些实现类,即作了些准备,还没真正开始。

接着看代码,如下

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
复制代码

是不是眼熟?熟悉的方法又出来了。通过名字也能猜到这是跟异常相关的,可作自定义实现,不再深入。