Spring AnnotationConfig 启动流程分析

背景

        Spring基本属于目前离不开的开发框架了,Spring IOC更是如此,IOC主要的实现,控制反转,将所有Bean归于Spring容器中进行管理,通过使用CurrentHashMap来保证其线程安全性。使得开发对Bean的管理与使用变得及其方便、简洁。那么我们今天主要来盘一盘Spring中AnnotationConfigApplicationContext类启动流程。当面试提起这个问题时,分分钟拿下对手。


 

 区别

        首先我们在获取SpringBean时常用是ApplicationContext,反而BeanFactory用得少,那么先聊一聊BeanFactory与ApplicationContext的区别,我们根据其类图可以看出

         BeanFactory是获取Bean对象,顾名思义,专注于Bean的获取,读取Bean的配置文档,管理Bean的加载与其实例化,维护Bean的生命周期。ApplicationContext则实现了BeanFactory,继承了其功能的情况下,实现了其它的加强方法。提供更为完整的框架功能,事件传递、国际化、项目内文件资源访问等功能。

实现

        再来扒一扒AnnotationConfig 的实现,前面图中我们了解到AnnotationConfig是ApplicationContext的实现子类,包含了其相关方法。接下来着重通过执行流程图来进行分析其操作运行步骤。

1.首先其启动,最先是获取ApplicationContext。根据AnnotationConfig对象实现并获取

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);

2.首先在构造函数中优先执行父类,会根据AnnotationConfig的父类GenericApplicationContext去创建默认的实体工厂,也就是DefaultListableBeanFactory,该Bean工厂主要负责后续的Bean注入以及实例化IOC的beanDefinitionMap存储对象。

// 父类构造函数初始化对象源码
public GenericApplicationContext() {
	this.beanFactory = new DefaultListableBeanFactory();
}

 3.再执行AnnotationConfig中自身的构造函数,创建实例化两个对象。一个读取对象AnnotationBeanDefinitionReader,后续会执行doRegisterBean(注册Bean)方法。一个扫描对象ClassPathBeanDefinitionScanner。

// 当前调用的构造函数源码
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
	this();
	register(annotatedClasses);
	refresh();
}

// 创建扫描对象与读取对象的无参构造函数
public AnnotationConfigApplicationContext() {
	this.reader = new AnnotatedBeanDefinitionReader(this);
	this.scanner = new ClassPathBeanDefinitionScanner(this);
}

4.走到AnnotationBeanDefinitionReader对象中的doRegisterBean方法中,方法详细如同中解析。会对Bean的相关注解进行判断处理。注册前使用BeanDefinitionHolder去包装Bean对象。也就是源码中最后的三行代码。

<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
		@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
	AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
	if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
		return;
	}
	abd.setInstanceSupplier(instanceSupplier);
	ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
	abd.setScope(scopeMetadata.getScopeName());
	String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
	AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
	if (qualifiers != null) {
		for (Class<? extends Annotation> qualifier : qualifiers) {
			if (Primary.class == qualifier) {
				abd.setPrimary(true);
			}
			else if (Lazy.class == qualifier) {
				abd.setLazyInit(true);
			}
			else {
				abd.addQualifier(new AutowireCandidateQualifier(qualifier));
			}
		}
	}
	for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
		customizer.customize(abd);
	}
    // 使用BeanDefinitionHolder 包装对象
	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

5.BeanDefinitionReaderUtils注册在第四步被包装了的Bean对象。

注册时按源码的执行逻辑是,根据bean的名称去调用注册Bean方法registerBeanDefinition,如果有别名则注册别名。该方法会去选择程序最开始创建的BeanFactory对象。在这里也就是第二步中GenericApplicationContext类创建的DefaultListableBeanFactory对象。

public static void registerBeanDefinition(
		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
		throws BeanDefinitionStoreException {
	// Register bean definition under primary name.
	String beanName = definitionHolder.getBeanName();
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
	// Register aliases for bean name, if any.
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias : aliases) {
			registry.registerAlias(beanName, alias);
		}
	}
}

6.调用注册之后就会选择Bean工厂去完成该行为,这里通过AnnotationConfig父类创建的是DefaultListableBeanFactory,那么自然就会走入该类方法,然后将beanDefinition存放在IOC容器中,也就是beanDefinitionMap中去。

最后通过this.beanDefinitionMap.put存放Bean到Spring IOC容器中。

 完成启动,之后即可以通过ApplicationContext上下文获取IOC容器中的对象,如下例代码可以打印出所有容器中Bean的ID,即注册时的Bean Name

String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}

总结

         以上即AnnotationConfig类的启动流程,阅读源码的一个心得是,首先大量的设计模式,单例、策略、模板等等,一定要对设计模式有所见解,否则很难读懂其含义。 其次就是可以通过Debug了解程序源码运行的走向,进行一个较为全面的了解。最后必然就是高效的学习+输出了。今日分享到此,下回见。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值