文章目录
回顾:
上一篇讲了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来造轮子。