前提:假设在你的项目或者磁盘上有X和Y两个类,X是被加了spring注解的,Y没有加spring的注解;也就是正常情况下当spring容器启动之后通过getBean(X)能正常返回X的bean,但是如果getBean(Y)则会出异常,因为Y不能被spring容器扫描到不能被正常实例化;
①
[^1]当spring容器启动的时候会去调用ConfigurationClassPostProcessor这个bean工厂的后置处理器完成扫描,关于什么是bean工厂的后置处理器下文再来详细解释;spring完成扫描的具体源码放到后续的文章中再来说。阅读本文读者只需知道扫描具体干了什么事情即可;其实所谓的spring扫描就是把类的信息读取到,但是读取到类的信息存放到哪里呢?比如类的类型(class),比如类的名字,类的构造方法。可能有读者会有疑问这些信息不需要存啊,直接存在class对象里面不就可以?比如当spring扫描到X的时候Class clazzx = X.class
;那么这个classx里面就已经具备的前面说的那些信息了,确实如此,但是spring实例化一个bean不仅仅只需要这些信息,还有我上文说到的scope,lazy,dependsOn等等信息需要存储,所以spring设计了一个BeanDefintion的类用来存储这些信息。故而当spring读取到类的信息之后②
[2]会实例化一个BeanDefinition的对象,继而调用这个对象的各种set方法存储信息;每扫描到一个符合规则的类,spring都会实例化一个BeanDefinition对象,然后把根据类的类名生成一个bean的名字(比如一个类IndexService,spring会根据类名IndexService生成一个bean的名字`indexService`,spring内部有一套默认的名字生成规则,但是程序员可以提供自己的名字生成器覆盖spring内置的,这个后面更新),`③`[3]继而spring会把这个beanDefinition对象和生成的beanName放到一个map当中,key=beanName,value=beanDefinition对象;至此上图的第①②③步完成。
这里需要说明的是spring启动的时候会做很多工作,不仅仅是完成扫描,在扫描之前spring还干了其他大量事情;比如实例化beanFacctory、比如实例化类扫描器等等,这里不讨论,在以后的文章再来讨论
用一段代码和结果来证明上面的理论
Appconfig.java
@ComponentScan("com.enjoy.beanDefinition")
@Configuration
public class Appconfig {
}
X.java
@Component
public class X {
public X(){
System.out.println("X Constructor"