美文网首页
SpringBoot注解扩展篇(1)-关于容器启动

SpringBoot注解扩展篇(1)-关于容器启动

作者: Terminalist | 来源:发表于2018-06-07 00:46 被阅读120次

用了一段时间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>();

这个方法里面主要做了三件事:

  1. 首先设置其启动日期startupDate和活动标志active,这个在容器启动后也会有使用;
  2. initPropertySources():初始化一些属性设置, 用来给子类去定义实现的,这是第一个扩展点,如果需要自己实现自己的ApplicationContext,并且在验证之前为系统属性设置一些值可以在子类中实现此方法;
  3. 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,已经很晚了,今天总结到此结束,后面的流程慢慢接着分析,文中有不懂之处或者写的不明之处希望读者提出,我们一起解决,共同进步;

相关文章

网友评论

      本文标题:SpringBoot注解扩展篇(1)-关于容器启动

      本文链接:https://www.haomeiwen.com/subject/ymamxftx.html