Spring——IOC源码解析(注解示)

概述

spring2x版本,提供了注解配置的方式,本文会基于注解的方向分析SpringIOC模块的整体流程。

主要关注注解方式启动spring与xml配置方式有何变化?

demo

1
2
3
4
5
6
7
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

继承关系

Spring——IOC源码解析(注解示)_2020-04-16-15-20-31.png

可以看到相较于 ClassPathXmlApplicationContextFileSystemXmlApplicationContext 来说AnnotationConfigApplicationContext 这个类的辈分好像更高一些

源码分析

1
2
3
4
5
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}

这里能看出与 ClassPathXmlApplicationContext 有很多不一样的地方了。

this()

首先看this:

1
2
3
4
5
6
public AnnotationConfigApplicationContext() {
//注解bean读取器
this.reader = new AnnotatedBeanDefinitionReader(this);
//注解bean扫描器
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

AnnotatedBeanDefinitionReader, ClassPathBeanDefinitionScanner这两个核心类比较重要。

AnnotatedBeanDefinitionReader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
// 设置registry,本质上是annotationConfigApplicationContext实例
this.registry = registry;
// 新建conditionEvaluator,用于处理Conditional注释
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
/**
* 注册AnnotationConfigProcessors,包括:
* ConfigurationClassPostProcessor
* AutowiredAnnotationBeanPostProcessor
* RequiredAnnotationBeanPostProcessor
* CommonAnnotationBeanPostProcessor
* PersistenceAnnotationBeanPostProcessor
* EventListenerMethodProcessor
* DefaultEventListenerFactory
*/
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

看到这里应该联想到想到 XmlBeanDefinitionReader

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)会把一些自动注解处理器加入到AnnotationConfigApplicationContext下的BeanFactory的BeanDefinitions中

ClassPathBeanDefinitionScanner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 设置registry,本质上是annotationConfigApplicationContext实例
this.registry = registry;

if (useDefaultFilters) {
/**
* 为@Component注册默认的过滤器
* 隐含地注册包含@Component的注释,包括:@Repository, @Service, @Controller
* 也支持@ManagedBean以及@Named注释
* 保存在List<TypeFilter> includeFilters里
* AnnotationTypeFilter
*/
registerDefaultFilters();
}
// 设置environment,StandardEnvironment
setEnvironment(environment);
// 设置resourceLoader,本质上还是annotationConfigApplicationContext实例
setResourceLoader(resourceLoader);
}

配置扫描过滤,设置环境变量,加载资源文件。

GenericApplicationContext

我们还记得 ClassPathXmlApplicationContext 的父类 AbstractXmlApplicationContext 吗?

GenericApplicationContext 有何区别?

子类的构造方法执行之前肯定会先执行父类的构造方法:

1
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
1
2
3
4
public GenericApplicationContext() {      
//这个bean的相关知识请参考之前的文章
this.beanFactory = new DefaultListableBeanFactory();
}

DefaultListableBeanFactory 这个背景强大的类是不是很熟悉。。。

scan()

接着看 scan 方法:

1
2
3
4
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}

啊啊,这里就可以看到这里调用的是this 注解的bean扫描器 ClassPathBeanDefinitionScannerscan 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
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 方法。

doScan()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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;

再来看它扫描包的方法 findCandidateComponents

扫描包

findCandidateComponents()

这个方法判断忽略了Filter指定包不扫描

1
2
3
4
5
6
7
8
9
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
//判断是否使用Filter指定忽略包不扫描
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
//扫描包
return scanCandidateComponents(basePackage);
}

所以还要进入 scanCandidateComponents 方法看一下,不过之前我们有必要先认识一个接口 MetadataReader

MetadataReader

根据扫描包反射生成的元数据接口

1
2
3
4
5
6
7
8
9
 public interface MetadataReader {

Resource getResource();

ClassMetadata getClassMetadata();

AnnotationMetadata getAnnotationMetadata();

}

第一个返回Resource就不必多说了,就是配置类的资源对象。第二个第三个根据名字我们可以猜到是类的元数据和注解的元数据 可以看一下它们两个的方法。

scanCandidateComponents()

这个方法就很清晰了:

  1. 组装扫描路径
  2. 遍历根据路径获取资源对象数组
  3. 根据反射生成的元数据是否有@Conditional一系列的注解,然后是否满足注册Bean的条件
  4. 满足Bean的条件,用 ScannedGenericBeanDefinition 类去组装他,存入 BeanDefinition 的Set集合。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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;
}

我们发现这里就已经出现了 BeanDifinition,上文xml配置启动我们是在refresh()方法。。。

创建 BeanDefinition

让我们回到 doScan() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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;
}

我们又把 Set<BeanDefinition> 里的数据进行一番处理后又存入 Set<BeanDefinitionHolder>。 接下来我们来看一下 registerBeanDefinition 方法

注册 Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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的方法,之前的文章已经解析过了,最终会将注册了的 BeanDefinition 存入 beanDefinitionMap这里就很明白了:

相对与xml配置的方式,注解式将创建bean,注解bean的过程放在 scan() 里执行

那我们再看看 refresh 方法。。。

refresh()

首先整个方法进来以后跟使用XML的时候是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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不同

其实之前的解析xml、创建Bean、注册Bean都是在 obtainFreshBeanFactory 方法进行,我们花了好长的时间去解析,现在再看注解式它的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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());
}

它的核心方法 refreshBeanFactory 只有这么点了,它来自 GenericApplicationContext 类,是我们的 AnnotationConfigApplicationContext的父类,在对比下 ClassPathXmlApplicationContext

1
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
1
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

refreshBeanFactoryAbstractApplicationContext 类的抽象方法,

GenericApplicationContextAbstractRefreshableApplicationContext 分别重写了这个方法,而这两个类分别是 AnnotationConfigApplicationContextClassPathXmlApplicationContext 的父类和超父类

初始化

初始化就和xml配置一样了,就不赘述了。

思考

我们在日常开发中有时候xml配置和注解是通用的,比如我们在xml里配置扫描:

1
<context:component-scan base-package="com.qn"/>

我们还记得在上篇文章中的一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// default namespace 涉及到的就四个标签 <import />、<alias />、<bean /> 和 <beans />
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 解析 default namespace 下面的几个元素
parseDefaultElement(ele, delegate);
}
else {
// 解析其他 namespace 的元素
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 解析其他 namespace 的元素
delegate.parseCustomElement(root);
}
}

context 标签会走进 parseCustomElement 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = this.getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
} else {
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
} else {
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
}
}

它最终会走进parse方法:

1
2
3
4
5
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = this.findParserForElement(element, parserContext);
return parser != null ? parser.parse(element, parserContext) : null;
}

接着走:

1
2
3
4
5
6
7
8
9
10
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
String basePackage = element.getAttribute("base-package");
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ",; \t\n");
ClassPathBeanDefinitionScanner scanner = this.configureScanner(parserContext, element);
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
this.registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}

Set<BeanDefinitionHolder> 是不是很熟悉, scanner.doScan(basePackages) 是不是很熟悉。。。

参考文档

SpringIOC源码解析(基于注解)

文章目录
  1. 1. 概述
  2. 2. demo
  3. 3. AnnotationConfigApplicationContext
    1. 3.1. 继承关系
    2. 3.2. 源码分析
      1. 3.2.1. this()
        1. 3.2.1.1. AnnotatedBeanDefinitionReader
        2. 3.2.1.2. ClassPathBeanDefinitionScanner
        3. 3.2.1.3. GenericApplicationContext
      2. 3.2.2. scan()
        1. 3.2.2.1. doScan()
        2. 3.2.2.2. 扫描包
          1. 3.2.2.2.1. findCandidateComponents()
          2. 3.2.2.2.2. MetadataReader
          3. 3.2.2.2.3. scanCandidateComponents()
        3. 3.2.2.3. 创建 BeanDefinition
        4. 3.2.2.4. 注册 Bean
      3. 3.2.3. refresh()
        1. 3.2.3.1. 与xml不同
      4. 3.2.4. 初始化
  4. 4. 思考
  5. 5. 参考文档
|