Spring-IOC容器随笔

前言

这篇文章,我打算对我认知的ioc容器做一个总结。方便大家也来了解一下ioc容器,以便于激起大家求知的欲望而自己去翻阅ioc容器的源码。如果有错误的地方,也请不吝指正,共同进步。

ioc容器何时被创建

1、第一种方式,容器启动

容器启动的话,我们都知道容器中web.xml的载体是ServletContext,web.xml中可以配置listener来监听容器的启动。如果我们想使用spring,那么就需要配置ContextLoaderListener,这是spring启动的入口,容器启动的时候,触发listener-class的创建,调用contextInitialized()方法,在ContextLoaderListener的contextInitialized()方法总最终会调用AbstractApplicationContext.refresh(),开始容器的创建。

2、第二种方式,springboot启动

springboot启动稍微简单一点,启动的入门在启动类的run方法里,run方法里会调用refreshContext(context)进行容器的启动,最终还是调用AbstractApplicationContext.refresh()。

扩展点

ApplicationContextInitializer

在spring初始化应用上下文ApplicationContext的时候,可以实现ApplicationContextInitializer接口,来对应用上下文进行扩展。例如添加一个beanFactoryPostProcessor。这是spring的第一个扩展点。例如:

@Slf4j
public class MyApplicationContextInitializer implements ApplicationContextInitializer {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        FirstBeanDefinitionRegistryPostProcessor processor = new FirstBeanDefinitionRegistryPostProcessor();
        applicationContext.addBeanFactoryPostProcessor(processor);
        log.info("自定义BeanDefinitionRegistryPostProcessor已加载进spring上下文");
    }
}
# 在yml或者propeties配置一下
context:
  initializer:
    classes: com.yameng.spring.beanFactoryPostPrecessors.MyApplicationContextInitializer

这样就可以在上下文初始化的时候,把我们自定义的FirstBeanDefinitionRegistryPostProcessor加载进beanFactoryPostPrecessors缓存。
这里有个问题。在初始化的时候,spring是怎么找到我们自定义的MyApplicationContextInitializer并执行initialize()方法的。答案就是,其实spring在配置上下文环境的时候,会去所有的propertyResolver中寻找key为context.initializer.classes的value,这个context.initializer.classes就是我们在配置文件中配置的key。
spring提供的所有的扩展点,几乎都是这么实现的(总结下来就是,你扩展的类实现了扩展的接口,要么以固定的key配置在配置文件里,要么带上spring的官方注解被扫描加载到beanDefinitionMaps缓存里,然后spring根据接口的类型去beanDefinitionMaps轮询获取)
我们前文这个扩展的例子,因为上下文初始化的时候,BeanFactory还没有创建,所以我们只能采用第一种方式,也就是配置在配置文件的形式。

ImportBeanDefinitionRegistrar

@Slf4j
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      log.info("MyImportBeanDefinitionRegistrar.registerBeanDefinitions is run");
    }
}
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class DemoConfig {
}

这也是ioc容器里一个比较重要的拓展点, 在spring的bean上,使用@Import注解注册一个ImportBeanDefinitionRegistrar,就可以在registerBeanDefinitions方法里注册一些自己的bean定义。我一般是用来给某些bean注册一些依赖的类和属性。
看到这里,需要弄明白一个问题:ImportBeanDefinitionRegistrar的加载原理,什么时候被调用。
首先,MyImportBeanDefinitionRegistrar是依赖于DemoConfig,是在DemoConfig被注册到beanDefinitionMaps的时候被调用的,DemoConfig被扫描到是因为有@Configuration注解。所以一个大概流程就是:
当扫描@Configuration注解的时候,扫描到了DemoConfig,然后把DemoConfig封装成ConfigurationClass,然后发现这个类有@Impot注解,拿到@impot注解里的类,发现是ImportBeanDefinitionRegistrar类型,放到ConfigurationClass的importBeanDefinitionRegistrars属性里,这是一个map,然后遍历调用registerBeanDefinitions()方法。
我没有讲的很仔细,你需要自己弄明白,什么时候去扫描@Configuration注解,哪个类干的这个活,在ioc的哪个流程里。自己撸一撸源码就明白了,后面有时间我会内容补充进来。
后续我会做一个ioc容器的流程图,哪些扩展点在哪个环节。

你可能感兴趣的:(Spring-IOC容器随笔)