文章结构
阅读本篇文章之前建议您首先查看上方两篇文章,因为本篇主要是描述了基于注解的方式和基于xml配置文件方式的异同点:
- 源码阅读环境的搭建
- 首先简单描述了
bean容器AnnotationConfigApplicationContext - 然后源码执行的第一步就是关于包扫描一块的东西,也就是我们比较熟悉的注解
@ComponentScan - 扫描到了各个带有注解的类之后就是读取类呀、反射呀什么的来加载类
- 当
bean已经加载完后就是bean的注册逻辑了
相比较与之前两篇文章的长篇大论,这篇文章的篇幅要少很多,这主要得益于Spring的设计精妙和代码之优雅准备工作
本文会基于注解的方向分析SpringIOC模块的整体流程,在阅读本篇文章之前建议您先阅读基于XML分析的两篇文章: SpringIOC源码解析(上),SpringIOC源码解析(下)
Demo工程
本次源码分析的demo工程我已经准备好了,大家可自行前往以下地址下载
https://github.com/shiyujun/spring-framework
本次工程复用了之前工程的包cn.shiyujun.service中的接口和实现类,同时新增了一个基于注解的配置类,此类在cn.shiyujun.config包下
@Configurationpublic class AnnotationConfig {@Beanpublic IOCService iocService(){return new IOCServiceImpl();}}
然后就是启动类了,启动类在cn.shiyujun.demo包下
public class AnnotationIOCDemo {public static void main (String args[]){ApplicationContext context = new AnnotationConfigApplicationContext("cn.shiyujun.config");IOCService iocService=context.getBean(IOCService.class);System.out.println(iocService.hollo());}}
AnnotationConfigApplicationContext继承关系

再次拿出之前的一张图片,可以看到相较于ClassPathXmlApplicationContext和FileSystemXmlApplicationContext来说AnnotationConfigApplicationContext这个类的辈分好像更高一些

接着我们看一下它的方法
我们会发现除了register注册bean的方法以外,有一个scan方法,有没有感觉很熟悉。@CompantScan用过没,他们之间什么关系,在启动类中new AnnotationConfigApplicationContext的时候传的一个包名是不是跟这个有关系?带着疑问往下看吧
源码分析
构造方法
源码分析第一站就是进入如下构造方法
public AnnotationConfigApplicationContext(String... basePackages) {this();scan(basePackages);refresh();}
千万不要小瞧上方简简单单的三行代码,我们整篇文章都会基于这三行代码来展开
首先看this
public AnnotationConfigApplicationContext() {//注解bean读取器this.reader = new AnnotatedBeanDefinitionReader(this);//注解bean扫描器this.scanner = new ClassPathBeanDefinitionScanner(this);}
同时子类的构造方法执行之前肯定会先执行父类的构造方法,所以还有父类
GenericApplicationContext的构造方法
public GenericApplicationContext() {//这个bean的相关知识请参考之前的文章this.beanFactory = new DefaultListableBeanFactory();}
注册bean
接着看scan方法
public void scan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");this.scanner.scan(basePackages);}
可以看到这里调用的是bean扫描器ClassPathBeanDefinitionScanner的scan方法
public int scan(String... basePackages) {//获取当前注册bean的数量int beanCountAtScanStart = this.registry.getBeanDefinitionCount();//往下看doScan(basePackages);if (this.includeAnnotationConfig) {//注册配置处理器AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}//返回此次注册的数量return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);}
接着往下看doScan方法
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();//遍历需要扫描的包路径for (String basePackage : basePackages) {//先跟进去看,下面的方法先忽略Set<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}
扫描包
public Set<BeanDefinition> findCandidateComponents(String basePackage) {//判断是否使用Filter指定忽略包不扫描,@Component注解里可以以传入filter数组,xml中也可以传入标签if (this.componentsIndex != null && indexSupportsIncludeFilters()) {return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);}else {//扫描包return scanCandidateComponents(basePackage);}
在接着往下看之前,我们有必要先认识一个东东,MetadataReader。这个接口有三个方法
public interface MetadataReader {Resource getResource();ClassMetadata getClassMetadata();AnnotationMetadata getAnnotationMetadata();}
第一个返回Resource就不必多说了,就是配置类的资源对象。第二个第三个根据名字我们可以猜到是类的元数据和注解的元数据
可以看一下它们两个的方法


接着往下看
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {//组装扫描路径(组装完成后是这种格式:classpath*:cn/shiyujun/config/**/*.class)String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;//根据路径获取资源对象Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}if (resource.isReadable()) {try {//根据资源对象通过反射获取资源对象的MetadataReader,具体就不展开说了MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);//查看配置类是否有@Conditional一系列的注解,然后是否满足注册Bean的条件,关于这个知识点可以参考我之前的文章:https://mp.weixin.qq.com/s/RXYIh_g5iU1e3liK-8n5zAif (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setResource(resource);sbd.setSource(resource);if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}candidates.add(sbd);}else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + resource);}}}else {if (traceEnabled) {logger.trace("Ignored because not matching any filter: " + resource);}}}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}else {if (traceEnabled) {logger.trace("Ignored because not readable: " + resource);}}}}catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;}
创建BeanDefinition
现在回到开始的doScan方法
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();//遍历需要扫描的包路径for (String basePackage : basePackages) {//获取所有符合条件的BeanDefinitionSet<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {//绑定BeanDefinition与ScopeScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());//查看是否配置类是否指定bean的名称,如没指定则使用类名首字母小写String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//下面两个if是处理lazy、Autowire、DependencyOn、initMethod、enforceInitMethod、destroyMethod、enforceDestroyMethod、Primary、Role、Description这些逻辑的if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}//检查bean是否存在if (checkCandidate(beanName, candidate)) {//又包装了一层BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);//检查scope是否创建,如未创建则进行创建definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);//重点来了,往下看registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}
注册bean
到了一个比较重要的节点了,跟着上文的registerBeanDefinition方法走
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);}public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {String beanName = definitionHolder.getBeanName();// 注册bean,往下看,这里的registry来自AnnotationConfigApplicationContext构造的时候构造的 ClassPathBeanDefinitionScanner传入的 thisregistry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());//如果存在别名则循环注册别名,逻辑跟上方差不多,就不展开了String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias: aliases) {registry.registerAlias(beanName, alias);}}}
其实这个注册bean的方法是DefaultListableBeanFactory的方法,之前的文章已经解析过了,大体就是下面这么个流程
@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(...);}}BeanDefinition oldBeanDefinition;// 所有的 Bean 注册后都会被放入到这个beanDefinitionMap 中,查看是否已存在这个beanoldBeanDefinition = this.beanDefinitionMap.get(beanName);// 处理重复名称的 Bean 定义的情况if (oldBeanDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {// 如果不允许覆盖的话,抛异常throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +"': There is already [" + oldBeanDefinition + "] bound.");}else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {// 用框架定义的 Bean 覆盖用户自定义的 Beanif (this.logger.isWarnEnabled()) {this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +oldBeanDefinition + "] with [" + beanDefinition + "]");}}else if (!beanDefinition.equals(oldBeanDefinition)) {// 用新的 Bean 覆盖旧的 Beanif (this.logger.isWarnEnabled()) {this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +oldBeanDefinition + "] with [" + beanDefinition + "]");}}else {// log...用同等的 Bean 覆盖旧的 Beanif (this.logger.isInfoEnabled()) {this.logger.info("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + oldBeanDefinition +"] with [" + beanDefinition + "]");}}// 覆盖this.beanDefinitionMap.put(beanName, beanDefinition);}else {// 判断是否已经有其他的 Bean 开始初始化了.注意,"注册Bean" 这个动作结束,Bean 依然还没有初始化 在 Spring 容器启动的最后,会 预初始化 所有的 singleton beansif (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;if (this.manualSingletonNames.contains(beanName)) {Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);updatedSingletons.remove(beanName);this.manualSingletonNames = updatedSingletons;}}}else {// 将 BeanDefinition 放到这个 map 中,这个 map 保存了所有的 BeanDefinitionthis.beanDefinitionMap.put(beanName, beanDefinition);// 这是个 ArrayList,所以会按照 bean 配置的顺序保存每一个注册的 Bean 的名字this.beanDefinitionNames.add(beanName);// 这是个 LinkedHashSet,代表的是手动注册的 singleton bean,this.manualSingletonNames.remove(beanName);}this.frozenBeanDefinitionNames = null;}if (oldBeanDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}}
现在回到文章开始的三句代码
public AnnotationConfigApplicationContext(String... basePackages) {this();scan(basePackages);refresh();}
可以看到,只剩最后一个refresh()方法了,如果看过之前文章的同学可能都已经知道这里面是什么东西了
refresh()
首先整个方法进来以后跟使用XML的时候是一样的
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 记录容器的启动时间、标记“已启动”状态、检查环境变量prepareRefresh();// 初始化BeanFactory容器、注册BeanDefinitionConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 beanprepareBeanFactory(beanFactory);try {// 扩展点postProcessBeanFactory(beanFactory);// 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法invokeBeanFactoryPostProcessors(beanFactory);// 注册 BeanPostProcessor 的实现类registerBeanPostProcessors(beanFactory);// 初始化MessageSourceinitMessageSource();// 初始化事件广播器initApplicationEventMulticaster();// 扩展点onRefresh();// 注册事件监听器registerListeners();// 初始化所有的 singleton beansfinishBeanFactoryInitialization(beanFactory);// 广播事件finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// 销毁已经初始化的的BeandestroyBeans();// 设置 'active' 状态cancelRefresh(ex);throw ex;}finally {// 清除缓存resetCommonCaches();}}}
与XML的不同点
obtainFreshBeanFactory()方法
还记得在之前的文章中列出了好几万行代码来解析这个方法,但是回忆一个这个方法是干啥的来着,创建bean容器,但是呢,bean容器在scan方法里就已经创建好了,所以这里就没必要再进行额外的逻辑了,你看现在它的代码现在多简单
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {refreshBeanFactory();ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled()) {logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);}return beanFactory;}protected final void refreshBeanFactory() throws IllegalStateException {if (!this.refreshed.compareAndSet(false, true)) {throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");}this.beanFactory.setSerializationId(getId());}
再接下来,其实就没了,有一个重点就是初始化,但是初始化逻辑是放在这个Spring家族的超级富二代DefaultListableBeanFactory身上的。
心得体会
源码阅读入门很难,面对几十兆几百兆的代码不是一时半会就能看明白的,也不是一遍两遍debug就能搞懂的。阅读源码,一定要静下心来花上几个小时甚至几天的时间来钻研。
一通百通,当你研究明白一部分以后,再去看另外的部分,就好像有人推着你走一样,无比的顺利
