bean放入到ioc容器 需要先解析bean的xml或者相关注解获取到beanDefinition然后根据beanDefinition生产对应的bean实例并放入容器中。
容器中都是存放的都是单例bean
BeanDefinition相关接口和类的介绍
-
- BeanDefinition继承了AttributeAccessor(存放和取一些key-value)和BeanMetadataElement(可以配置bean的元数据资源,即获取beanDefinition的数据源,比如xml的标签和注解)
- 2.一级实现类或者子接口:AbstractBeanDefinition(完成了大部分的方法)和AnnotatedBeanDefinition(子接口,增加了获取注解元数据和方法的元数据)
- 3.加载beanDefinition:BeanDefinitionReader接口读取资源loadBeanDefinition,AbstractBeanDefinitionReader(实现了其大部分方法),XmlBeanDefinitionReader(对xml进行解析获取bean)。AnnotatedBeanDefinitionReader是一个单独的类和BeanDefinitionReader没有关系,但是其提供的方法和其相似
- 注册:AliasRegistry(将beanName和别名进行映射),BeanDefinitionRegistry(提供了对beanName和BeanDefinition的赠删改查的操作)
因为IOC容器想要最终获取beanName和BeanDefinition需要三步骤
- 1.BeanDefinition的Resource定位
- 2.Resource的载入
- 3.注册进入IOC容器
笔者对注解和xml 两个解析细节统一进行详细阐述.
此处我们讲的beanDefinition是我们开发人员通过xml配置 或者@Component等注解注入的beanDefinition,这里不包含spring在启动时自动注入的一系列beanDefinition
笔者终于找到了spring.xml和我们自定义注解bean是什么时候处理
BeanDefinition的Resource定位
-
- AbstractApplicationContext--->refresh()---> invokeBeanFactoryPostProcessors(beanFactory)-->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors-->
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)-->ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry-->processConfigBeanDefinitions-->ConfigurationClassParser parser () --->ConfigurationClassParser processConfigurationClass--->ConfigurationClassParser doProcessConfigurationClass
- AbstractApplicationContext--->refresh()---> invokeBeanFactoryPostProcessors(beanFactory)-->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors-->
-
- 在ConfigurationClassParser类的 方法 processConfigurationClass,将我们的@Configuration类包装成source
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
SourceClass asSourceClass(@Nullable Class> classType) throws IOException {
if (classType == null) {
return new SourceClass(Object.class);
}
try {
// Sanity test that we can reflectively read annotations,
// including Class attributes; if not -> fall back to ASM
for (Annotation ann : classType.getAnnotations()) {
AnnotationUtils.validateAnnotation(ann);
}
return new SourceClass(classType);
}
catch (Throwable ex) {
// Enforce ASM via class name resolution
return asSourceClass(classType.getName());
}
}
上述代码就是获取@Configuration注解类的class同时校验该class上面的注解是否配置正确即注解的属性是否合理。并把他包装成source然后交给doProcessConfigurationClass进行处理
Resource和beanDefinition的载入
概述
- 1 ConfigurationClassParser doProcessConfigurationClass(),即对获取到的@Configuration 注解进行处理,而我们的@ImportResource注解则是放在启动类上,我们导入spring.xml是依靠@ImportResource,即此时在把相关soucre载入
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
首先递归处理任何成员(嵌套)类,即从sourceClass获取内部类看是否属于@configuration,属于就递归调用doProcessConfigurationClass处理子类
processMemberClasses(configClass, sourceClass);
处理@PropertySource 注解,将properties文件属性注入到具体bean中,但是前提是environment必须是ConfigurableEnvironment 否则忽略
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
处理@ComponentScan annotations
Set 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 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) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(
holder.getBeanDefinition(), this.metadataReaderFactory)) {
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
处理 any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
处理 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);
}
}
处理 individual @Bean methods
Set beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 处理实现接口的方法
processInterfaces(configClass, sourceClass);
// 处理 父类, 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;
}
其中@PropertySources和@PropertySource 以及@ComponentScans和@ComponentScan 分别是集合和单个元素的关系
处理类中类
-
- 首先递归处理任何成员(嵌套)类,即从sourceClass获取内部类看是否属于@configuration,属于就递归调用doProcessConfigurationClass处理子类
处理@PropertySource 注解
- 1.将properties文件属性注入到具体bean中,但是前提是environment必须是ConfigurableEnvironment 否则忽略
- 2.最终把PropertySource放入propertySourceList
- 3.更多的细节请看第三节
处理@ComponentScan
- 1.首先从@ComponentScans和@ComponentScan获取,而我们的启动类上有注解@SpringBootApplication,其内部定义了默认的scanBasePackages()和scanBasePackageClasses(),我们可以从中获取到@ComponetScan
- 2.
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
的主要逻辑是设置ClassPathBeanDefinitionScanner的属性:registry,resourceLoader,beanNameGenerator,scopedProxyMode,resolverClass,resourcePattern,includeFilters(必须是@Component和@ManagedBean注解才可以),excludeFilters(componentScan注解所在的类不需要扫描),lazyInit,basePackages。 -
- 其中获取basePackages的逻辑是:从basePackages和basePackageClasses获取他们的包名字放入到集合中,如果集合还是为空就把我们注解类的包名放入进去。
-
-
scanner.doScan(StringUtils.toStringArray(basePackages));
这是最终开始扫描的我们包的操作
-
-
-
ClassPathScanningCandidateComponentProvider
类的Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath)
是获取到了我们注入的bean资源
-
- 6.获取到资源后(也就是basePackages下面的class文件),通过文件流加载class然后获取class和注解的相关属性。然后获取注解是@Component和@ManagedBean的类
-
- 将上述获取的候选类包装成ScannedGenericBeanDefinition,继续判断该类是否不依赖其他bean且该类要么是具体的实现类 要么是抽象类的同时其方法上面还有@Lookup的注解
- 8.通过scopeMetadataResolver获取ScopeMetadata 即判断是否是单例 同时是否需要包装成proxy默认是不需要。
- 9 .依次判断候选BeanDefinition是否是AbstractBeanDefinition和AnnotatedBeanDefinition 然后分别调用
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
和AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
其中前者将beanDefinitionDefaults设置到候选BeanDefinition,后者是将注解上面的一些属性设置到候选BeanDefinition - 10.将beanName和候选BeanDefinition包装成definitionHolder,同时看是否需要代理然后将该候选bean注册到beanFactory,但是目前看@bean 和@component 都没有提供ScopeMetadata 的属性配置
- 11.
ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass)
就是创建bean的proxy的地方。 - 12.创建动态代理的方式是把原先的BeanDefinition和targetBeanName(在原来的名字上加个前缀)注册到beanFactory中,且该BeanDefinition的AutowireCandidate和Primary属性被置为false
- 13.创建一个proxyDefinition 其beanClass是ScopedProxyFactoryBean,然后将原先的BeanDefinition和proxyDefinition 进行绑定 最终返回一个definitionHolder是proxyDefinition 和originalBeanName组成的
- 14 若返回的bean是属于@Component ,@ComponentScan ,@Import,@ImportResource.继续调用ConfigurationClassParser的parse方法进行重新处理
- 15我们通过上述可知@ComponentScan 有ScopedProxyMode 可以指定类是否启用代理。
处理 @Import 注解
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
- 0.也会坚持是否有循环导入,寻找的@Import,ImportSelector接口,ImportBeanDefinitionRegistrar接口 都是作为@Import一样处理
- 1.可以导入@Configuration,ImportSelector接口,ImportBeanDefinitionRegistrar接口
或者常规的 regular component classes 。 - 2.@ImportSelector可以返回一些列的需要导入的类
- 3.@ImportBeanDefinitionRegistrar 可以直接向beanFactory注册beanDefinition
- 4.经过测试@Configuration类都会被Spring用Cglib增强,但是具体在哪增加笔者尚未知晓@Configuration类下面的@Bean 不会被增强,且Component类下面的@Bean注解不会注入SpringIOC容器
-
- 下面是@Import ImportSelector 类 importingClassMetadata是我们@Import注解所在的类,最终可以导入我们想导入的类
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
System.out.println(importingClassMetadata.toString());
String[] importString = new String[]{"testImport.ImportBean"};
return importString;
}
}
-
- 下面是ImportBeanDefinitionRegistrar 可以在bean实例化前bean进行增删改查
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition =new RootBeanDefinition();
((RootBeanDefinition) beanDefinition).setBeanClass(ImportBean.class);
registry.registerBeanDefinition("xujieBean",beanDefinition);
}
}
处理@ImportResource
-
- 主要作用就是把xml的地址和readerClass放入importedResources集合,留作后期解析使用
处理@Bean
- 1.获取当前正在处理的class的方法上有bean的,获取到MethodMetadata,然后将MethodMetadata和当前class 包装成BeanMethod 加入到当前class的beanMethods
processInterfaces(configClass, sourceClass)
- 看样子是处理当前configClass实现的接口中的子类且加@Bean的方法(该注解放在父类或者子类方法都可以),但是这个方法至今笔者没太看懂他的意义
Process superclass,
-1 .即处理当前configclass 是否还有父类且非接口,如果有继续按照之前的流程进行处理。
此篇文章还留有疑问,后期笔者会在研读过程中进行解说
1.缺少spring.xml在哪一步加载
2.@configuration在哪一步开始进行了动态代理
- 何时处理beanMethods集合中的@Bean方法
4 .如何让propertySource生效
总结:doProcessConfigurationClass处理了类中类,@PropertySource,@ComponentScan,@Import,@ImportResource,@Bean,父类和子类,
其中只有@ComponentScan这一块遇到@Component注解才会注册bean,其他都存入其内部集合中留待真正的注册beanDefinition,详情请关注下篇文章。