正文共:3713 字 16 图
预计阅读时间:10 分钟
看到标题的老铁可能觉得,不就是引用 ApplicationContext 吗?这么简单还用写文章来说一下,有意思吗?老铁你别急,先看完内容再来评价一番。在项目中我们如果要引用 ApplicationContext 无非有两种方式。 1. 通过声明 ApplicationContext 属性,然后用 ApplicationContext 注解标示 2. 通过实现 ApplicationContextAware 接口,然后重写setApplicationContext 方法,然后声明全局变量并赋值。没错就是这两种方法,老规矩先上图。
我们先来看看项目结构,我先采用第一种方法来实现。我们看下程序执行结果。
可以看到程序调用结果,我们顺利的拿到了 ApplicationContext,并打印了项目中所有的 beanNames。
接下来再来看看采用第二种方法的项目的结构。
再来看看程序执行的结果是什么样的。
可以看到程序的执行结果和第一种是一模一样的。
那么重点来了,你可以看到我打印了 ApplicationContext 中所有的 beanNames,然而里面却没有 ApplicationContext 这个 Bean,那 Spring 是怎么帮我们拿到它的实例的呢。有没有一点好奇呢?接下来我来带你进入源码看一看吧。 首先我们先来看一看第一种方法的时候,Spring 是怎么帮我们处理的吧。其实如果你看过我前面的文章应该就能知道了。
先看上面一幅图,这是一幅 Spring 实例化 Bean 的流程图,我们知道如果是单例的 Bean,且不是懒加载的话,Spring 在启动的时候就会帮我们把所有的 Bean 实例化并缓存,随后我们就可以使用了。
请看上图中最后一步也就是 populateBean 方法,此方法就是 Spring 帮我们设置实例属性的方法。
由于此方法过长,而我们只用关注我图中画红框部分即可,此处会执行 Spring 自定义的属性设置后置处理器。如果你还记得前面我打印的所有的 beanNames 的话,里面有一个 AutowiredAnnotationPostProcessor,此方法就会执行这个后置处理器,并设置 Bean 实例的属性值。
上图就是后置处理器用来处理标注了 @Autowired 注解的处理流程,其中BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法会根据我们定义的属性的类型去 Spring 中缓存的 Bean 集合查找类型与之匹配的 Bean,并返回其 beanName,而我们的 ApplicationContext 是不在缓存当中的,这个可以从文章开始处,我打印的 beanNames 集合看到。
那么就会执行第二次查找,也就是 for 循环的部分,而我们的 ApplicationContext 就在 resolvableDependencies 集合中,为什么呢?来看一幅图,你就明白了。
如果你还记得前面我画的 Spring 实例化流程图,请注意绿色文字部分,就是执行的这个方法,此方法就是设置 beanFactory 的一些属性,注意图中红框部分,在这里注册了 ApplicationContext,我们来看看这个代码里面是什么吧。
你是不是有一种豁然开朗的感觉,此处的 resolvableDependencies 就是我们前面代码 for 循环里面的集合。Spring 在初始化的时候就已经帮我们注册好了,那么我们在循环的时候就一定可以获取 ApplicationContext的实例。
到此第一种方法获取 ApplicationContext 实例的源码我们就讲完了,下面再来看看第二种吧。还请看下还是上面 prepareBeanFactory 方法的那幅图,奥秘也在里面哦,请注意绿色框部分。
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
此处也添加了一个后置处理器,让我们来看看它的主要方法吧。
重点关注画红框部分方法。
可以看到方法最后一步就是判断,如果当前 bean 的实例是属于 ApplicationContextAware,那么就调用 bean.setApplicationContext 方法,而参数正是 ApplicationContext,这个时候就会回调我们实现了 ApplicationContextAware 接口重写的方法,然后把参数传递给我们的类。
那你一定很好奇,这个类是什么时候被调用的对吧。其实还要看前面 Spring 实例化流程图,流程图的最后一步是 populateBean 方法,但是却不是终点,其实接下来还有一个方法就是 initializeBean 方法。
我们来看看 initializeBean 方法,重点关注红框部分。
你会发现又是后置处理器的调用,没错,Spring 全靠这些后置处理器来处理和扩展了。那么此处的后置处理器就是 ApplicationContextAwareProcessor
通过这个后置处理器,就可以完成方法的回调,从而达到我们的目的。你一定很好奇此处实例化的 Bean 是谁。其实这个也可以从文章开始我们打印的 beanNames 集合中看到,就是 org.springframework.context.event.internalEventListenerProcessor 这个 Bean。
那么这个 Bean 是什么时候被加入到 Spring 中的呢。我们来看一段代码,你就明白了。
public AnnotationConfigApplicationContext() { //实例化一个添加了注解的Bean的,Bean定义读取器 this.reader = new AnnotatedBeanDefinitionReader(this); //实例化一个类扫描器 this.scanner = new ClassPathBeanDefinitionScanner(this);}
上面是 AnnotationConfigApplicationContext 的无参构造方法。
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); }
接下里调用了 AnnotatedBeanDefinitionReader 的构造方法。然后继续调用重载的构造方法。
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);}
注意最后一段代码,此处用来向 Spring 注册后置处理器。
由于此方法过长,我们重点关注红框部分,首先是判断是否包含一个 bean,如果不包含则注册 EventListenerMethodProcessor 后置处理器。我们来看看这个常量的定义。
public static final String EVENT_LISTENER_PROCESSOR_BEAN_NAME ="org.springframework.context.event.internalEventListenerProcessor";
是不是就是我们在开始的时候打印的 beanNames 集合中的一个呢?有的同学说了,我们注册的后置处理器是 EventListenerMethodProcessor 和 ApplicationContextAware 有什么关系呢。
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware
可以看到 EventListenerMethodProcessor 这个类的声明,是不是就明白了呢,所以 Spring 在实例化这个 Bean 的时候,就能根据以下代码回调我们的接口了。
if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}
到此处第二种方法的源码我们也分析完了,各位老铁,现在是不是会觉得有点意思呢。