用了一段时间Springboot了,但直到半月前才去研究其内部实现,今天开始,把自己的这段时间的所思所得记录在此,希望自己以后碰到相关问题,能顺着这个思路去找解决方案.
玩Spring 肯定就是先从容器初始化开始走,下面我贴出我学习时的测试demo,按照这个思路我们慢慢往下缕!
@Test
public void test09() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigAware.class);
log.info("ioc container is start...{}", applicationContext);
applicationContext.close();
}
接下来,进入的构造函数,可以看到以下内容:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
这里其实没什么好说的,无非是将被@Configuration注解修饰的类传进来,然后创建一个AnnotationConfigApplicationContext对其进行加载,重点在于refresh()这个方法,它才是主要干活的,我们进入这个方法一探究竟.
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
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.
destroyBeans();
// Reset 'active' flag.
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();
}
}
}
方法很长,但这是spring 容器启动的全流程,先看synchronized (this.startupShutdownMonitor)
: 它使用了对象锁startUpShutdownMonitor,在文章开篇的测试代码中我们就看到,启动容器之后,我们调用了 applicationContext.close()
方法,其实在close方法上也是有synchronized (this.startupShutdownMonitor)
的逻辑的,我们不妨看下源码:
public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
if (this.shutdownHook != null) {
try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
}
catch (IllegalStateException ex) {
}
}
}
}
在这里使用synchronized (this.startupShutdownMonitor)
有两个好处:
1.refresh()方法和close()方法都使用了startUpShutdownMonitor对象锁加锁,这就保证了在调用refresh()方法的时候无法调用close()方法,反之亦然,避免了冲突;
2.使用对象锁可以减小了同步的范围,只对不能并发的代码块进行加锁,提高了整体代码运行的效率;
接下来,我们继续接着refresh()方法往下走,看到执行prepareRefresh()
的流程,看下源码,我们一步一步来分析:
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment
initPropertySources();
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
这个方法里面主要做了三件事:
- 首先设置其启动日期startupDate和活动标志active,这个在容器启动后也会有使用;
- initPropertySources():初始化一些属性设置, 用来给子类去定义实现的,这是第一个扩展点,如果需要自己实现自己的ApplicationContext,并且在验证之前为系统属性设置一些值可以在子类中实现此方法;
- getEnvironment().validateRequiredProperties():获取当前的Environment,如果没有则创建一个StandardEnvironment,并且进行属性校验,校验属性的合法,代码逻辑实现如下:
public void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key : this.requiredProperties) {
if (this.getProperty(key) == null) {
ex.addMissingRequiredProperty(key);
}
}
if (!ex.getMissingRequiredProperties().isEmpty()) {
throw ex;
}
}
如果在前面初始化容器的时候设置了环境需要的属性值,但是系统环境中没有找到,那么这里会报错的,可以写个小的demo测试一下:
public class TestValidateRequiredProperties {
public static void main(String[] args) {
CustomApplicationContext context = new CustomApplicationContext();
}
}
class CustomApplicationContext extends AbstractApplicationContext {
CustomApplicationContext() {
getEnvironment().setRequiredProperties("test");
refresh();
}
@Override
protected void refreshBeanFactory() throws BeansException, IllegalStateException {
}
@Override
protected void closeBeanFactory() {
}
@Override
public ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException {
return null;
}
}
当你在此处定义了一个属性值test,但是spring初始化的Environment里面是没有这个值的,那么此时就是输出以下内容:
Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [test]
怎么让它正常输出嘞? 很简单,继续往下看,只需要改下main方法的逻辑并且加上一个对应的app.xml文件即可:
public static void main(String[] args) {
Properties properties = System.getProperties();
properties.setProperty("test", "app.xml");
CustomApplicationContext context = new CustomApplicationContext();
4.this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();初始化一个集合,用于保存容器中早期的事件;
接下来,继续看ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()的实现,这一步的流程主要如下,获取Bean工厂.
1.refreshBeanFactory();刷新或者创建beanFactory;
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
如果存在BeanFactory,这个地方也是通过加锁的方式进行BeanFactory的判断,足以见得世界上最优秀的代码对细节的把握;存在的话就将单例bean容器清空并且将之前的BeanFactory设置为null,
然后通过DefaultListableBeanFactory beanFactory = createBeanFactory()
方法创建新的BeanFactory,并且设置BeanFactory唯一的Id;
customizeBeanFactory(beanFactory)
,主要设置BeanFactory的相关属性,先解释下源码:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
this.allowBeanDefinitionOverriding
:是否允许覆盖同名称的不同定义的对象;
this.allowCircularReferences
:是否允许Bean之前存在循环依赖;但是只是在这两个地方进行了判空的操作,别的什么都没实现,真正的实现是在子类中进行覆盖的,我们可以根据这个方法的注视描述看下子类的覆盖,这里就不再多说了;
接下来我们再看loadBeanDefinitions(beanFactory)
的实现,它主要是加载beanDefinition,这个步骤就是通过初始化AnnotatedBeanDefinitionReader来读取配置的;
1.获取AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionReader及BeanNameGenerator;
2.若BeanNameGenerator不为空,则AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionReader使用设置的BeanNameGenerator,若为空,则取各自默认BeanNameGenerator;并且注册一个internalConfigurationBeanNameGenerator,这是spring容器启动时最先初始化的内部Bean之一;
3.ScopeMetadataResolver与BeanNameGenerator相同逻辑;ScopeMetadataResolver用于解析@Scope注解;
4.注册自定义添加的annotatedClass到AnnotatedBeanDefinitionReader中,ClassPathBeanDefinitionScanner扫描指定包路径下所有class包;
5.取configLocations配置,先当作AnnotatedClass进行注册,若找不到类,则当作包路径用以扫描BeanDefinition;
6.接下来主要看reader.register(clazz);具体实现在这:
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
1.首先根据@Conditional注解判断是否需要注册;
2.获取类上的@Scope注解并解析其元数据;
3.返回一个definitionHolder,并通过这个方法applyScopedProxyMode生成scope的代理;
4.最后将definitionHolder注册到容器中;
大体流程就是这样,内部细节大家自行查看,最后就是通过ConfigurableListableBeanFactory beanFactory = getBeanFactory()
操作返回beanFactory,其实也就是通过obtainFreshBeanFactory()这个方法后,ApplicationContext就已经拥有了BeanFactory的所有功能了.
ok,已经很晚了,今天总结到此结束,后面的流程慢慢接着分析,文中有不懂之处或者写的不明之处希望读者提出,我们一起解决,共同进步;
网友评论