美文网首页
【Spring】Ioc容器的实现(二)-- Applicatio

【Spring】Ioc容器的实现(二)-- Applicatio

作者: eejron | 来源:发表于2017-10-08 15:44 被阅读0次

上篇我们讲过,Ioc有两种使用方式,一种是使用BeanFactory,另一种是ApplicationContext。这篇文章,我们就是来讲第二种的实现。ApplicationContext对比前面一种方式拓展了很多功能,实际应用中我们主要也是使用这种方式。

Example

public class ApplicationContextIoc {

    public static void main(String[] args) {

        GenericApplicationContext context = new GenericApplicationContext();

        PropertyValue pv = new PropertyValue("name","Cat");
        MutablePropertyValues mpvs = new MutablePropertyValues();
        mpvs.addPropertyValue(pv);

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(Animal.class);
        bd.setPropertyValues(mpvs);

        context.registerBeanDefinition("animal",bd);
        
        context.refresh();//整个流程主要多了这个方法

        Animal animal = context.getBean(Animal.class);
        animal.say();

    }

    public static class Animal {

        private String name;

        public Animal() {
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void say(){
            System.out.println("Hello " + name);
        }
    }
}

这段代码大体上跟前面一篇类似,只是多出了一个refresh()方法。那么ApplicationContext和BeanFactory的异同在哪里?

GenericApplicationContext结构

先来看下GenericApplicationContext的结构。

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

    private final DefaultListableBeanFactory beanFactory;
   
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
            
        this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
    }
   
}

很明显,调用registerBeanDefinition()时其实还是由DefaultListableBeanFactory去实现,此处GenericApplicationContext它只是作为一个代理类存在而已。同样的getBean()方法也是由BeanFactory内部去实现的。

refresh()方法

来重点看下refresh()的逻辑,它是在父类AbstractApplicationContext定义,里面共有11个方法,在这里大致把这个方法的流程走一遍。

    AbstractApplicationContext.class

    @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.
            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();
            }
        }
    }

prepareRefresh()

设置容器的激活、关闭状态以及做一些校验动作,如果未将容器状态设置为已激活就去获取bean则会抛出一个未激活的异常。

obtainFreshBeanFactory()

上面我们讲到过ApplicationContext只是一个代理类而已,它对bean的一些管理还是由BeanFactory去实现的,此处便是去获取BeanFactory。获取BeanFactory时是通过getBeanFactory()去操作的,这是一个抽象方法,具体的逻辑是由子类GenericApplicationContext去实现。代码如下:

AbstractApplicationContext.class
@Override
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;


GenericApplicationContext.class
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
    return this.beanFactory;
}

可以看出来,其实BeanFactory是通过子类去创建,如果需要其它不同的BeanFactory只需要在子类中创建合适的BeanFactory即可,而已不需要修改这部分的代码,符合了“开放-闭合原则”。此处的BeanFactory是GenericApplicationContext的构造方法创建的。

prepareBeanFactory(beanFactory)

对BeanFactory做相关的配置,为后续BeanFactory的一些操作做准备。

postProcessBeanFactory(beanFactory)

设置BeanFactory的后续处理,该方法用protected修饰,且默认实现为空,目前spring重写该方法的子类跟web环境有关,一般用于拓展web环境。

invokeBeanFactoryPostProcessors(beanFactory)

调用BeanFactory的后处理器,它运行的时机是在bean定义全部加载到容器之后,bean的创建之前。这个主要用于修改容器的bean定义。与它相关的两个接口如下:

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

这两个接口其实是一个继承关系,两个实现的功能是相似的,前者通过BeanFactory去修改bean定义,而后者直接通过BeanDefinitionRegistry去修改而已。

registerBeanPostProcessors(beanFactory)

向容器注册bean的后处理器,这些后处理器在bean实例化后,运行初始化方法前、后分别进行回调。这里的初始化方法有两种形式:

  1. bean实现了InitializingBean接口所对应的afterPropertiesSet()方法
  2. bean里面通过init-method(xml或注解)标记的方法

与bean后处理器相关的接口如下

public interface BeanPostProcessor {
    //初始化前方法回调
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    //初始化后方法回调
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

initMessageSource()

初始化国际化工具类MessageSource

initApplicationEventMulticaster()

初始化事件广播器,用于事件的发布。

onRefresh()

该方法用protected修饰且默认实现为空,属于模板方法。目前重写该方法的都与web环境有关

registerListeners()

注册监听器,用于发布广播事件后的回调。

finishBeanFactoryInitialization(beanFactory)

这部分的主要内容是将非延迟加载的单例bean全部实例化

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }

        // Register a default embedded value resolver if no bean post-processor
        // (such as a PropertyPlaceholderConfigurer bean) registered any before:
        // at this point, primarily for resolution in annotation attribute values.
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
                @Override
                public String resolveStringValue(String strVal) {
                    return getEnvironment().resolvePlaceholders(strVal);
                }
            });
        }

        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        }

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // 冻结bean定义,表示不可再修改
        beanFactory.freezeConfiguration();

        // 实例化所有非懒加载的单实例bean
        beanFactory.preInstantiateSingletons();
    }

看最后一行代码,实例化bean的逻辑其实还是在BeanFactory中去实现,在此只是单纯的调用而已。所以说ApplicationContext只是作为 一个代理类去拓展BeanFactory的功能。

finishRefresh()

这个容器执行refresh()的最后一个方法,用于初始化容器生命周期的处理器,让容器启动、销毁等进行回调。并刷新容器的启动状态给生命周期处理回调,另外还发布ContextRefreshedEvent事件,让对应的监听器来进一步的处理。

总结

GenericApplicationContext作为BeanFactory的拓展容器,实现了更强大的功能,这些功能主要针对的是BeanFactory,bean及容器的生命周期进行拓展,提供了回调的入口让用户能进更细粒度的进行容器操作。但对于基础的bean定义管理,实例化这些实际上还是由BeanFactory去实现的,GenericApplicationContext只是作为一个代理类对BeanFactory去增强其功能而已。

其实refresh()这个方法里面内容的细节还很多,本文只是走了大概的流程。有时间再来具体分析啦。

相关文章

网友评论

      本文标题:【Spring】Ioc容器的实现(二)-- Applicatio

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