Spring源码调用链分析篇(二)

回顾:

上一篇讲了spring到的调用链,只讲到了AnnotationConfigApplicationContext初始化完成。今天主要分析,spring是如何扫描,如何注册我们的bean到容器的。

1、(spring的扫描)annotationConfigApplicationContext.register

		AnnotationConfigApplicationContext annotationConfigApplicationContext =
				new AnnotationConfigApplicationContext();
//		annotationConfigApplicationContext.scan("com.bin.li");
		annotationConfigApplicationContext.register(AppConfig.class);

1、接着会调到这一行代码,看这个方法的入参。可以传一个类,或者一个配置类。或者多个。

this.reader.register(annotatedClasses);

2、接着往下走,循环调用不说了。

public void register(Class<?>... annotatedClasses) {
		for (Class<?> annotatedClass : annotatedClasses) {
			registerBean(annotatedClass);
		}
	}

3、接着会调到这个方法来。看入参只有与一个 其他都为空。再看入参名称 annotatedClass 加了注解的Class对吧
4、第一行代码,会创建这个对象AnnotatedGenericBeanDefinition 这个就是bean的描述器,描述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);
		}
		

5、再看这一行代码。跳过就是spring加载的时候,需要排除的包在这里跳过。

	if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

6、再来看这几行代码。
第一行,直接跳过,因为初始化的时候我们只传了annotatedClass 其他都为空
第二行 ,得到穿过来类的作用域,因为我们可以传配置类,也可以传普通类大家可以自己试
第三行,设置作用域 不重要不说了
第四行,如果name不为空,我们这里为空。然后根据abd获取我们穿过来类的名字。也不重要

abd.setInstanceSupplier(instanceSupplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

7、这一行代码。挺重要。因为前面入参可以传配置类,也可以传普通类。所以spring要在这判断是否 懒加载@lazy ,是否加了@DependsOn啊等等等 这些注解,处理完以后会将这些信息放大都第一行new的对象中去。

AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

接下来几行代码 因为我们都传的null 最开始我们只传了一个annotatedClass过来对吧,所以就不看了。
接着来看这一行代码。
这个BeanDefinitionHolder又是什么呢?
这也是一种数据结构。其实这就是一个Map结构的BeanDefinition 里面放了 KEY value 这里的Key就是abd value就是beanName 这里的beanName就是上一步得到的,这里比较简单。就不说了。

		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

重点来看这一行代码,注册BeanDefinition上一篇已经深深的讲过了,这里不再重复。注册哪个BeanDefinition 就是我们穿进去的definitionHolder 注册到哪里呢?this.registry
何为 this.registry? 点上去看BeanDefinitionRegistry就是这个this.registry
那么何为BeanDefinitionRegistry上一篇也讲了,不说了。

	BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);

那么它怎么注册的呢? 来看这个方法,
BeanDefinitionHolder上面讲了,就是一个壳子,上一篇也讲到了。spring为了方便传参做了一个封装而已
第一行代码 String beanName = definitionHolder.getBeanName();把我们传进来那个类的名称拿出来。
第二行代码 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); 注册BeanDefinition就是把 我们传进来的这个类,或者配置类放在容器中。
这个方法很重要,上一篇也讲过。spring怎么把一个类放到容器,这里就不在重述。上一篇已经讲到了这个方法。
第三行代码,下面的不重要。对别名的操作,我们类可以取别名。。老司机应该的都知道。

	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);
			}
		}
	}

2、验证注册器。

上面讲到,执行完registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition())就把我们传进去得得得配置类放到了容器。现在来验证一下,是不是?
在这里插入图片描述
都这里 register方法就执行完了,下面到的代码都执行完了。AppConfig放到了容器。
想一下,这里只是把我们配置类放到了容器,还没有开始扫描。对吧。。

	//把spring所有前提环境准备好
		AnnotationConfigApplicationContext annotationConfigApplicationContext =
				new AnnotationConfigApplicationContext();
//		annotationConfigApplicationContext.scan("com.bin.li");
		annotationConfigApplicationContext.register(AppConfig.class);

3、初始化Spring环境,扫描。实例化Bean

1、接下来到了这一步,扫描包并实例化。

	//初始化spring环境(bean的实例化。)
		annotationConfigApplicationContext.refresh();

2、接着往下走
第一步 prepareRefresh(); 初始化环境,获取环境信息。这个不重要。
第二步 /获取到DefaultListableBeanFactory的子类。得到一个factory
具体怎么获取在 ConfigurableListableBeanFactory beanFactory = getBeanFactory();这一行代码,
从容器的父类去得到。大家可以自己去看一下。
所以spring得设计就很优雅,读这个源码就是很有意思。对吧

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			prepareRefresh();
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

接着走,这一步准备Bean工厂。

	prepareBeanFactory(beanFactory);

接着会调用这个方法。
第一步:设置类加载器。不说了这个
第二步:bean表达式解释器。为了能够让beanFactory解析bean这个也不说了(自行百度)
第三步:对象与string之间的转换 这个也不重要,
第四步:添加一个后置处理器。 那么添加这个ApplicationContextAwareProcessor后置处理器是干嘛的呢?
能够得到bean的各种*Aware 都有各自的作用。
何为后置处理器?
这里就是spring的扩展点之一,这个需要专门开一篇才能讲得了。可以理解为,有了这个后置处理器。程序员就能插手Bean的初始化过程。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		beanFactory.setBeanClassLoader(getClassLoader());
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

下面的添加忽略的列表这些不重要。

//添加自动注入 被忽略的列表
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

4、invokeBeanFactoryPostProcessors(beanFactory);

这一个方法 非常非常 非常 重要。
博客里 真的真的真的讲不了。。
这个方法名称就是 执行BeanFactoryPostProcessors
那到这一步,我们容器中有BeanFactoryPostProcessors嘛?有。上一篇敲黑板那里,重点讲那里。就是这个internalConfigurationAnnotationProcessor 它就是BeanFactoryPostProcessors的子类。
那么它有多牛逼呢?

invokeBeanFactoryPostProcessors(beanFactory);

先给个断点,看一下。执行这个方法之前。spring容器中,是7个对象。。。
在这里插入图片描述
执行这个方法之后,十个,就是我们项目中,所以加了注解的类,都被扫描出来了。并且放到了spring容器、、
在这里插入图片描述
spring是怎么完成的呢?这里博客里真的写不了。大家可以自己去看。。有什么问题!可以留言

学源码的思考

spring初始化看到这里。基本上已经掌握了spring的 30% 已经拿到所有类信息了。。接下来怎么办?还用我说嘛?
为什么要学源码?学源码牛逼啊。。
每个程序员都有自己的想法。
spring作为Java里最牛逼得框架。我觉得还是有必要学一下源码。
但是我们会用就好了,我们学源码干嘛?
学以致用,下一篇。我们要自己用Spring来造轮子。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值