文档章节

Spring 之 IoC 源码分析 (基于注解方式)

大王叫下
 大王叫下
发布于 08/19 11:48
字数 1532
阅读 249
收藏 5

一、 IoC 理论

IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI(Dependency Injection),即依赖注入。

二、IoC方式

Spring为IoC提供了2种方式,一种是基于xml,另一种是基于注解。

  • <Bean>标签来定义bean,进行管理。
  • @Bean注解来定义bean,进行管理。

本次文章我们就来分析下基于注解的IoC原理,在看文章之前我们可以带一些疑问,这样有助于我们更好的理解。

  1. @Bean是干什么用的?
  2. @Controller、@Service又是干啥的?
  3. @CompoentScan注解是怎么起作用的?
  4. Spring是怎么发现@Bean、@Controller、@Service这些注解修饰的类的?
  5. 发现之后是怎么注册到IOC容器中的?
  6. IOC容器到底是个啥?

三、源码分析

首先看下段代码:

AnnotationConfigApplicationContext aac =
				new AnnotationConfigApplicationContext("com.mydemo");

AnnotationConfigApplicationContext可以实现基于Java的配置类(包括各种注解)加载Spring的应用上下文。避免使用application.xml进行配置。相比XML配置,更加便捷。

3.1、类结构图

主要类或接口说明:

  • GenericApplicationContext——通用应用上下文,内部持有一个DefaultListableBeanFactory实例,这个类实现了BeanDefinitionRegistry接口,可以在它身上使用任意的bean definition读取器。典型的使用案例是:通过BeanFactoryRegistry接口注册bean definitions,然后调用refresh()方法来初始化那些带有应用上下文语义(org.springframework.context.ApplicationContextAware)的bean,自动探测org.springframework.beans.factory.config.BeanFactoryPostProcessor等。

  • BeanDefinitionRegistry——用于持有像RootBeanDefinition和 ChildBeanDefinition实例的bean definitions的注册表接口。DefaultListableBeanFactory实现了这个接口,因此可以通过相应的方法向beanFactory里面注册bean。GenericApplicationContext内置一个DefaultListableBeanFactory实例,它对这个接口的实现实际上是通过调用这个实例的相应方法实现的。

  • AbstractApplicationContext——ApplicationContext接口的抽象实现,没有强制规定配置的存储类型,仅仅实现了通用的上下文功能。这个实现用到了模板方法设计模式,需要具体的子类来实现其抽象方法。自动通过registerBeanPostProcessors()方法注册BeanFactoryPostProcessor, BeanPostProcessor和ApplicationListener的实例用来探测bean factory里的特殊bean——对比1分析

  • AnnotationConfigRegistry——注解配置注册表。用于注解配置应用上下文的通用接口,拥有一个注册配置类和扫描配置类的方法。

3.2 构造函数

        //默认构造函数,初始化一个空容器,容器不包含任何 Bean 信息,需要在稍后通过调用其register()
	//方法注册配置类,并调用refresh()方法刷新容器,触发容器对注解Bean的载入、解析和注册过程
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	
	public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
		super(beanFactory);
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	
	//最常用的构造函数,通过将涉及到的配置类传递给该构造函数,以实现将相应配置类中的Bean自动注册到容器中
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		//调用无参构造函数,初始化AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner
		this();
		register(annotatedClasses);
		refresh();
	}

	
	//该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的Spring Bean,将其注册到容器中
	public AnnotationConfigApplicationContext(String... basePackages) {
	        //初始化ClassPathBeanDefinitionScanner和AnnotatedBeanDefinitionReader
		this();//step1
		//扫描包、注册bean
		scan(basePackages);//step2
	        refresh();//step3
	}

主要属性:

  • AnnotatedBeanDefinitionReader——BeanDefinition解析器用来解析带注解的bean

  • ClassPathBeanDefinitionScanner——bean的扫描器 用来扫描类

  • 注册解析传入的配置类(使用类配置的方式进行解析)

  • 调用容器的refresh方法初始化容器

这里我们用的是最后一种构造函数,即传入一个包路径。

3.3 IoC 之 构造函数初始化

首先看step1,调用了本类的无参构造函数:

public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

然后初始化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
我们来看下ClassPathBeanDefinitionScanner的构造函数

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
		this(registry, true);
	}

继续跟踪下去,最后调用的是这个方法:

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		//为容器设置加载Bean定义的注册器
		this.registry = registry;

		//是否使用默认过滤规则
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		//设置环境
		setEnvironment(environment);
		//为容器设置资源加载器
		setResourceLoader(resourceLoader);
	}

这里面最主要的是registerDefaultFilters()方法,初始化spring扫描默认过滤规则,对应@ComponentScan注解,如果没有自定义规则,就初始化默认过滤规则。
这里调用的是ClassPathScanningCandidateComponentProvider类中的registerDefaultFilters()方法:

//向容器注册过滤规则
@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
	//向要包含的过滤规则中添加@Component注解类
	//@Service和@Controller都是Component,因为这些注解都添加了@Component注解
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	//获取当前类的类加载器
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
		//向要包含的过滤规则添加JavaEE6的@ManagedBean注解
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
		logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
	}
	try {
		//向要包含的过滤规则添加@Named注解
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
		logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}

这里面有两个关键变量:

  • private final List<TypeFilter> includeFilters = new LinkedList<>();

  • private final List<TypeFilter> excludeFilters = new LinkedList<>();

includeFilters表示要包含的注解,即只有包含includeFilters中的注解,才会被扫描
excludeFilters表示要排除的注解,即包含excludeFilters中的注解不会被扫描

在这个方法中,includeFilters集合中添加了@Component、JavaEE6的@ManagedBean和JSR-330的@Named注解
而excludeFilters集合没做任何变动,即没有要排除的注解

总结:
所以默认规则就是,只要包含了@Component、JavaEE6的@ManagedBean和JSR-330的@Named这3个注解中的任意一个,就会被扫描

下一篇文章,我们将看一下spring具体的包扫描过程Spring IoC 源码分析 (基于注解) 之 包扫描

© 著作权归作者所有

大王叫下
粉丝 12
博文 60
码字总数 46111
作品 0
杭州
私信 提问
深入聊一聊 Spring AOP 实现机制!

作者 | 张书康 责编 | 郭 芮 AOP(Aspect-Oriented Programming,即面向切面编程。Spring Aop 在 Spring框架中的地位举足轻重,主要用于实现事务、缓存、安全等功能。本篇主要是对源码进行深...

CSDN资讯
01/26
0
0
IOC/AOP工具 - jBeanBox

jBeanBox是一个微形但功能较齐全的IOC/AOP工具适用于JAVA7+,利用了Java的初始化块实现的Java配置代替XML。jBeanBox采用Apache License 2.0开源协议。 其他一些IOC/AOP框架的问题: 1)Sprin...

yong9981
2016/07/25
3.2K
15
SpringBoot的幕后推手...

一、背景   这两年随着微服务的盛行,SpringBoot框架水到渠成的得到了高曝光,作为程序猿的我们,现在要是不知道一点SpringBoot相关的东西,貌似出去找工作都会被深深地鄙视,不过在我们开...

Java工程师-Distance
2018/06/01
117
0
一个面试题引起的SpringBoot启动解析

分开描述各自的特征: Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring 的ioc和 aop,ioc 提供了依赖注入的容器, aop解决了面向横切面的编程...

Java大蜗牛
2018/08/08
107
0
Spring系列教程六:AOP详细讲解

AOP 概述 什么是 AOP AOP:全称是 Aspect Oriented Programming 即:面向切面编程。 AOP技术是对OOP技术的一种延伸,AOP是面向纵向,OOP是面向横向。简单的说它就是把我们程序重复的代码抽取...

我叫小糖主
05/19
59
0

没有更多内容

加载失败,请刷新页面

加载更多

反编译9.png图片还原

本文链接:https://blog.csdn.net/a1140778530/article/details/10528507 经常反编译apk文件找资源,9.png的文件处理起来很麻烦。 最近使用Ant自动编译打包app时,从别处搜罗来的9.png文件导...

shzwork
7分钟前
2
0
Shell脚本应用 – for、while循环语句

一、for循环语句 在实际工作中,经常会遇到某项任务需要多次执行的情况,而每次执行时仅仅是处理的对象不一样,其他命令相同。例如:根据通讯录中的姓名列表创建系统账号等情况。 当面对各种...

linux-tao
7分钟前
2
0
RPA风潮下企业财务工作模式的变革

RPA(机器人流程自动化)在财务领域的应用,正给企业财务带来前所未有的改变。 前RPA时代,财务领域面临的痛点 在RPA机器人应用之前,企业财务工作进程的推进,主要通过财务人员人工操作或信...

UiBot
12分钟前
2
0
Hive之命令行修改表注释

最近遇到一个需求,在不重建表的情况下,修改表的注释,hive有没有类似关系型数据库的SQL命令来修改呢,找了下,亲测有效,如下List-1 List-1 hive>use your_schemahvie>ALTER TABLE tabl...

克虏伯
12分钟前
2
0
是什么,它的作用是什么

在HTML文档的首部往往会有这么一句话<!DOCTYPE html>,许多时候我们忽视了它的存在,它实际上是一个声明,告诉浏览器用哪种HTML版本的规范来解读HTML文档。 尽管我们不给出这句声明浏览器照样...

前端老手
18分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部