SpringIOC源码解析(基于注解)

注意,看完这篇文章需要很长很长很长时间。。。

准备工作

本文会基于注解的方向分析SpringIOC模块的整体流程,在阅读本篇文章之前建议您先阅读基于XML分析的两篇文章: SpringIOC源码解析(上)SpringIOC源码解析(下)

Demo工程

本次源码分析的demo工程我已经准备好了,大家可自行前往以下地址下载

https://github.com/shiyujun/spring-framework

本次工程复用了之前工程的包cn.shiyujun.service中的接口和实现类,同时新增了一个基于注解的配置类,此类在cn.shiyujun.config包下

@Configuration
public class AnnotationConfig {
    @Bean
    public 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继承关系

再次拿出之前的一张图片,可以看到相较于ClassPathXmlApplicationContextFileSystemXmlApplicationContext来说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扫描器ClassPathBeanDefinitionScannerscan方法

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指定忽略包不扫描
		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-8n5zA
						if (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) {
		    //获取所有符合条件的BeanDefinition
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
             //绑定BeanDefinition与Scope
				ScopeMetadata 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.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

	//如果存在别名则循环注册别名,逻辑跟上方差不多,就不展开了
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias: aliases) {
			registry.registerAlias(beanName, alias);
		}
	}
}

其实这个注册bean的方法是DefaultListableBeanFactory的方法,之前的文章已经解析过了,大体就是下面这么个流程

@Override
public 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 中,查看是否已存在这个bean
   oldBeanDefinition = 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 覆盖用户自定义的 Bean
		if (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 覆盖旧的 Bean
		if (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 覆盖旧的 Bean
		if (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 beans
      if (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 保存了所有的 BeanDefinition
         this.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容器、注册BeanDefinition
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
      prepareBeanFactory(beanFactory);

      try {
         // 扩展点
         postProcessBeanFactory(beanFactory);
         // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 注册 BeanPostProcessor 的实现类
         registerBeanPostProcessors(beanFactory);

         // 初始化MessageSource
         initMessageSource();

         // 初始化事件广播器
         initApplicationEventMulticaster();

         // 扩展点
         onRefresh();

         // 注册事件监听器
         registerListeners();


         // 初始化所有的 singleton beans
         finishBeanFactoryInitialization(beanFactory);

         // 广播事件
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // 销毁已经初始化的的Bean
         destroyBeans();

         // 设置 '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就能搞懂的。阅读源码,一定要静下心来花上几个小时甚至几天的时间来钻研。

一通百通,当你研究明白一部分以后,再去看另外的部分,就好像有人推着你走一样,无比的顺利

本文分享自微信公众号 - Java学习录(Javaxuexilu)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏生信了

生信(七)生信中常用命令

(说明:我们拿到的bed文件时常是客户在Windows系统下编辑好的,其行尾是\r\n,在进行NGS分析前最好将其转换为Unix风格的行尾\n。)

8020
来自专栏upuptop的专栏

【趣学编程】linux常用命令(二)

9820
来自专栏upuptop的专栏

【趣学程序】集群之间配置SSH无密码登录

注意 假设以上配置是在A机器上配置的,目标机器为B。那么我们就可以在A机器上通过 ssh B的IP 访问B这台机器了 如果你想在B电脑上也可以免密登录A机器,那...

14020
来自专栏没有故事的陈师傅

shell编程中的循环脚本

本文中的部分脚本来源于网络,就不申明原创了,如果这些东西自己学会了,那就是属于自己的了。

7930
来自专栏upuptop的专栏

【趣学程序】Linux流的重定向

11720
来自专栏猿码优创

Spring Boot项目sheel脚本(启动、停止、重启、状态)

(adsbygoogle =window.adsbygoogle ||[]).push({});

18920
来自专栏猿码优创

Flume安装及部署

(adsbygoogle =window.adsbygoogle ||[]).push({});

33720
来自专栏生信了

Shell(一)好的操作习惯

前一段时间顺丰运维人员将生产数据库删除的传闻着实成为了新闻热词,如果传闻是真的,相信那位运维也是无心之过,可能只是一瞬的手误。但是代价太大了,业内人员都懂的。

7330
来自专栏upuptop的专栏

【趣学程序】Hadoop运行模式

注意:Namenode 和 ResourceManger 如果不是同一台机器,不能在 NameNode 上启动 yarn,应该在 ResouceManager ...

10720
来自专栏upuptop的专栏

【趣学程序】Linux基础命令

常用:/home /etc /mnt /root /opt /tmp /usr /var

17110

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励