spring源码IOC之bean扫描
一.什么是Ioc
我们都知道spring的两大核心功能:Ioc和Aop。Ioc(Inversion of controller)控制反转,顾名思义就是把控制权从我们程序员的手中抢到机器控制。回想之前初学Java我们都是在代码方法中自己new一个对象出来,此时新建对象这个的控制权是在我们手中。当我们使用spring之后,我们会感觉我们用@Controller、@Service、@Configuration等注解就可以在直接使用这个对象压根就没有使用new xxxx()就把对象给创建出来了,这就说明这个对象的控制权不由我们控制转交给了机器。
二.开始前的术语了解
2.1bean和bean定义(beanDefinitation)区别:bean(实例)是对象,beanDefinitation是对bean的描述(例如:有哪些属性、是否懒加载、初始化方法和销毁方法等),bean定义是对bean的描述。
bean的实例化和初始化:bean的实例化指的是对bean构建,bean的初始化是对bean中的属性进行赋值等操作。理解为bean的实例化是建一个房子,bean的初始化是对房子装修。
bean的后置处理器:在对bean初始化之前对bean进行操作(如属性复制等等)
beanFactory的后置处理器触发时机和作用: bean定义加载到容器中但是还没有实例化之前对bean进行操作。
BeanPostFactory和BeanFactoryPostFactory都可以对bean操作,他们之间的执行顺序是:
BeanFactoryPostProcessor—>构造方法—>BeanPostProcessor的before—>init-method—>BeanPostProcessor的after.
beanDefinationRegistryPostProcessor触发时机:beanDefinationRegistryPostProcessor了继承beanFactory,作用是在容器没有扫描bean定义之前,可以先越过容器加载通过postProcessBeanDefinitionRegistry方法把自己加上到容器里
三.Ioc-Bean扫描
bean扫描的流程图
相关继承图
四.Ioc大框架解析
1. 在分析源码之前我们先想象下,如果是我们要自己写这样的功能,我们会怎么做?
1.(有哪些)我们有哪些实体需要加到容器中 :1.1确定查找的范围 1.2哪些标志说明是这个是需要查找
2.(怎么找)知道需要的实体之后怎样把实体找出来
3.(如何加)如何给找到的实体添加到容器中
2.Spring的做法
// 创建一个spring的容器
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
// 进入注解版spring的上下文源码
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// 创建包含容器自定义bean定义信息以及类路径下bean定义信息扫描器 这里就是确定第一步中哪些标志需要找
this();
// 将传入类的信息注册到beanFactory中 为第二步和第三步做准备
register(componentClasses);
// spring的核心代码 对应第二和三步
refresh();
}
this()方法:获取spring容器中已经有的类
new AnnotatedBeanDefinitionReader(this);加载spring中已有的类,有5个大类
new ClassPathBeanDefinitionScanner(this); 加载类路径下需要扫描的类信息(有@Component注解) 至于为啥是这个注解我们后续详情的时候说明
register(componentClasses)方法:将传入的配置文件注册的Ioc容器中,以传入的类为基础查找需要的bean
这里可以看出是循环将传入的配置文件注册到Ioc容器中
refresh()方法:重中之重,里面很多思想和方法都是值得我们深究的,本次只解析我用到的方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备容器刷新的环境
prepareRefresh();
// 返回beanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 给bean工厂赋值需要用到的类
prepareBeanFactory(beanFactory);
try {
// spring提供的扩展方法给类实现后置处理器的功能 postProcessBeanFactory
postProcessBeanFactory(beanFactory);
// 注入spring的bean定义信息
invokeBeanFactoryPostProcessors(beanFactory);
// 实例化bean信息 Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// 初始化spring多播器Initialize event multicaster for this context.
initApplicationEventMulticaster();
// 这是一个空方法,实现一些特殊的类。在springboot中主要用于绑定tomcat,以及tomcat等事 件
onRefresh();
// 绑定容器中已有和程序自定义的事件监听器 onApplicationEvent
registerListeners();
// 完成容器中所有不是懒加载的bean
finishBeanFactoryInitialization(beanFactory);
// 完成刷新
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
五.Ioc框架详细源码说明
this()方法干了什么事情?
AnnotationConfigApplicationContext继承了GenericApplicationContext这个类,GenericApplicationContext的构造方法创建了beanFactory,DefaultListableBeanFactory这个bean工厂是最“具体”的工厂类。
在进入AnnotatedBeanDefinitionReader()方法之前beanFactory
debug形式进入代码
这个方法加载spring框架自带的容器信息,把自带的类通过registerPostProcessor转换成对应的BeanDefinitionHolder,AnnotatedBeanDefinitionReader()方法之后
我们可以了解到AnnotatedBeanDefinitionReader()方法是将spring自定义的类加载到容器中
bebug跟进ClassPathBeanDefinitionScanner():解决上面提到加载哪些标注的类
从这我们可以看出这个类主要是告诉spring要拦截带有Component注解的类,这也就是为什么我们标注的@Controller、@Service、@Configuration等注解会被拦截,因为他们的底层注解都有@Component
需要注意的是@Controller @Service @Repositry这些类需要依据包扫描的类路径下才行。
register(componentClasses) 这个方法干了什么事情?
代码跟进核心方法:
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
// 将类设置成单例
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
// 给传进来的注解生成bean定义信息获取属性信息 设置懒加载 首选、依赖
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));
}
}
}
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
}
// 将定义信息注入容器中
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
方法执行结束beanFactory信息:
可以看到spring将我们传入的主类文件解析并注入容器中,这个是一个循环的方法,在new AnnotationConfigApplicationContext()我们可以传入多个类。在springBoot中我们只配置一个主启动类
refresh()方法
此处我只解析我此次需要用到的方法:invokeBeanFactoryPostProcessors(beanFactory)、registerBeanPostProcessors(beanFactory)
invokeBeanFactoryPostProcessors(beanFactory)方法,调用流程
调用bean定义注册的后置处理器从容器中已有的bean定义信息中获取到@Configuration标注的bean定义信息放入Configuration缓存中
判别出有Configuration标注注解的类信息
获取出Configuration缓存之后进行排序,用于后面处理对这种类进行解析,解析这个方法是个核心的方法
解析配置类,根据配置类的beanName转换成Configuration类,再对这个类进行解析,此处有包扫描配置。
SpringBoot包扫描以及自定义包扫描的配置在这里设定。还有@PropertySource、@Import、@ImportResource、@Bean
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
这里获取到的配置包扫描路径和配置类的一致,只有在包扫描路径及其所有子包下的@Controller、@Service等才能生效
获取到@Bean注解的方法,对应上图的两个方法
解析完configuration中的信息,把获取的信息转换成bean定义信息。核心方法:this.reader.loadBeanDefinitions(configClasses);
看这个方法的处理过程
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
// 是否是import导入的,加载@Import的类
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 是否是bean导入的,加载@bean方法的类
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 是否是ImportResources导入,加载@ImportResources
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 常规加载
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
loadBeanDefinitionsForBeanMethod(beanMethod),加载@Bean定义的方法,获取相关的信息有包括bean的别名以及后续bean信息中的属性如@AutoWired 初始化和销毁方法
// 处理bean的一些初始信息 在前面配置类加载的步骤一样
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
// 获取@Autowired的属性并将值存储
Autowire autowire = bean.getEnum("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
boolean autowireCandidate = bean.getBoolean("autowireCandidate");
if (!autowireCandidate) {
beanDef.setAutowireCandidate(false);
}
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
String destroyMethodName = bean.getString("destroyMethod");
beanDef.setDestroyMethodName(destroyMethodName);
// 确定bean是单实例还是多实例。Spring默认为单实例。是否创建代理对象
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
if (attributes != null) {
beanDef.setScope(attributes.getString("value"));
proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}
}
// Replace the original bean definition with the target one, if necessary
BeanDefinition beanDefToRegister = beanDef;
if (proxyMode != ScopedProxyMode.NO) {
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
new BeanDefinitionHolder(beanDef, beanName), this.registry,
proxyMode == ScopedProxyMode.TARGET_CLASS);
beanDefToRegister = new ConfigurationClassBeanDefinition(
(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName);
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
configClass.getMetadata().getClassName(), beanName));
}
//将该bean的定义信息注入到Ioc容器中
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
解析完的配置文件存储,清除未解析(候选的)配置类,判断还有没有未解析的类。用的是do~while循环
之后在处理其他的后置处理器如实现(PriorityOrdered、PriorityOrdered)等。调用方法相似且博主案列不涉及就未展示有兴趣的小伙伴可自行跟踪
registerBeanPostProcessors(beanFactory)方法
注册BeanPostProcessors,bean的后置处理器在之前我们描述中说到BeanPostProcessors这个类是在bean实例化前后可以对bean进行一些操作。这个方法主要给beanFactory注入后置处理器,并没有实际的操作。为了给bean实例化做准备。
registerBeanPostProcessors(beanFactory, XXPostProcessors);
可以看出仅仅只是beanFactory.addBeanPostProcessor();
真正实现bean实例化在 finishBeanFactoryInitialization(beanFactory)–>beanFactory.preInstantiateSingletons()–>getBean(beanName)–>doGetBean(beanName)
六.总结
第一次看源码写博客在很多类以及概念上都有很多的不解,在书写的过程中或多或少都有不严谨出问题的情况,还请大家指正,共同进步。
接下来,我将继续更新创建bean对象和单例缓存池,以及三级缓存池解决bean创建时候的循环依赖问题。