spring启动流程 (3) BeanDefinition详解-全球焦点

2023-07-04 09:12:36 | 来源:博客园

BeanDefinition在Spring初始化阶段保存Bean的元数据信息,包括Class名称、Scope、构造方法参数、属性值等信息,本文将介绍一下BeanDefinition接口、重要的实现类,以及在Spring中的使用示例。

BeanDefinition接口

用于描述了一个Bean实例,该Bean实例具有属性、构造方法参数以及由具体实现提供的其他信息。


(资料图片仅供参考)

这是一个基础接口:主要目的是允许BeanFactoryPostProcessor获取和修改Bean实例属性和其他元数据。

封装以下信息:

AbstractBeanDefinition类

实现了BeanDefinition接口,具体的、完整的BeanDefinition基类,抽取出GenericBeanDefinition、RootBeanDefinition和ChildBeanDefinition的公共属性。

扩展的属性:

RootBeanDefinition类

继承AbstractBeanDefinition类。

RootBeanDefinition表示在运行时支持BeanFactory中指定Bean的合并BeanDefinition。它可能是由多个相互继承的原始BeanDefinition创建的,通常注册为GenericBeanDefinitions。RootBeanDefinition本质上是运行时的"统一"RootBeanDefinition视图。

RootBeanDefinition也可以用于在配置阶段注册各个BeanDefinition。然而,自Spring2.5以来,以编程方式注册BeanDefinition的首选方式是GenericBeanDefinition类。GenericBeanDefinition的优势是允许动态定义父依赖项,而不是将角色硬编码为RootBeanDefinition。

扩展的属性:

GenericBeanDefinition类

继承AbstractBeanDefinition类。

GenericBeanDefinition是用于构建标准BeanDefinition的一站式组件。与其他BeanDefinition一样,它允许指定一个类以及可选的构造方法参数和属性。另外,从父BeanDefinition派生可以通过parentName属性灵活配置。

通常,使用GenericBeanDefinition类来注册用户可见的BeanDefinition,后置处理器可能会对其进行操作,甚至可能重新配置parentName属性。如果父子关系恰好是预先确定的,请使用RootBeanDefinition和ChildBeanDefinition。

AnnotatedBeanDefinition接口

继承BeanDefinition接口。

扩展BeanDefinition接口,提供Bean的AnnotationMetadata,而不需要加载该类。

public interface AnnotatedBeanDefinition extends BeanDefinition {/** * Obtain the annotation metadata (as well as basic class metadata) * for this bean definition"s bean class. */AnnotationMetadata getMetadata();/** * Obtain metadata for this bean definition"s factory method, if any. */MethodMetadata getFactoryMethodMetadata();}

ScannedGenericBeanDefinition类

GenericBeanDefinition类的扩展,基于ASM ClassReader,实现了AnnotatedBeanDefinition接口,可以获取注解元数据。

这个类不会提前加载Bean Class。它从.class文件检索所有相关的元数据,并使用ASM ClassReader进行解析。

public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {private final AnnotationMetadata metadata;/** * Create a new ScannedGenericBeanDefinition for the class that the * given MetadataReader describes. * @param metadataReader the MetadataReader for the scanned target class */public ScannedGenericBeanDefinition(MetadataReader metadataReader) {this.metadata = metadataReader.getAnnotationMetadata();setBeanClassName(this.metadata.getClassName());setResource(metadataReader.getResource());}@Overridepublic final AnnotationMetadata getMetadata() {return this.metadata;}@Overridepublic MethodMetadata getFactoryMethodMetadata() {return null;}}

AnnotatedGenericBeanDefinition类

GenericBeanDefinition类的扩展,实现了AnnotatedBeanDefinition接口,可以获取注解元数据。

public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {private final AnnotationMetadata metadata;private MethodMetadata factoryMethodMetadata;public AnnotatedGenericBeanDefinition(Class beanClass) {setBeanClass(beanClass);this.metadata = AnnotationMetadata.introspect(beanClass);}public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata) {if (metadata instanceof StandardAnnotationMetadata) {setBeanClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());} else {setBeanClassName(metadata.getClassName());}this.metadata = metadata;}public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata,MethodMetadata factoryMethodMetadata) {this(metadata);setFactoryMethodName(factoryMethodMetadata.getMethodName());this.factoryMethodMetadata = factoryMethodMetadata;}@Overridepublic final AnnotationMetadata getMetadata() {return this.metadata;}@Overridepublic final MethodMetadata getFactoryMethodMetadata() {return this.factoryMethodMetadata;}}

Spring中使用BeanDefinition示例

注册componentClasses

AnnotationConfigApplicationContext启动代码:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();applicationContext.register(ServiceConfig.class);applicationContext.refresh();

AnnotationConfigApplicationContext在启动时可以使用register方法注册@Configuration类,本小节将从这个方法入手看一个BeanDefinition的使用示例:

public void register(Class... componentClasses) {Assert.notEmpty(componentClasses, "At least one component class must be specified");this.reader.register(componentClasses);}// reader.register(...)public void register(Class... componentClasses) {for (Class componentClass : componentClasses) {registerBean(componentClass);}}private  void doRegisterBean(Class beanClass, String name,Class[] qualifiers, Supplier supplier,BeanDefinitionCustomizer[] customizers) {// 构造方法中会解析AnnotationMetadata元数据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));// 解析Lazy,Primary,DependsOn,Role等属性AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);if (qualifiers != null) {for (Class 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);// 处理Scope的proxyModedefinitionHolder = AnnotationConfigUtils        .applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);// 注册到容器BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);}// BeanDefinitionReaderUtils.registerBeanDefinition(...)public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// Register bean definition under primary name.String beanName = definitionHolder.getBeanName();// 将Bean注册到BeanDefinitionRegistryregistry.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);}}}

此处的registry是AnnotationConfigApplicationContext对象,registerBeanDefinition方法的实现在GenericApplicationContext类:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {this.beanFactory.registerBeanDefinition(beanName, beanDefinition);}// beanFactory.registerBeanDefinition(...)public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();} catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);if (existingDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}this.beanDefinitionMap.put(beanName, beanDefinition);} else {if (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;removeManualSingletonName(beanName);}} else {// Still in startup registration phasethis.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}if (existingDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);} else if (isConfigurationFrozen()) {clearByTypeCache();}}

@Bean注解

@Bean注解注入的Bean最终在ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForBeanMethod方法注册BeanDefinition:

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {ConfigurationClass configClass = beanMethod.getConfigurationClass();MethodMetadata metadata = beanMethod.getMetadata();String methodName = metadata.getMethodName();// Do we need to mark the bean as skipped by its condition?if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {configClass.skippedBeanMethods.add(methodName);return;}if (configClass.skippedBeanMethods.contains(methodName)) {return;}AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);// Consider name and any aliasesList names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));String beanName = (!names.isEmpty() ? names.remove(0) : methodName);// Register aliases even when overriddenfor (String alias : names) {this.registry.registerAlias(beanName, alias);}// Has this effectively been overridden before (e.g. via XML)?if (isOverriddenByExistingDefinition(beanMethod, beanName)) {return;}// 创建ConfigurationClassBeanDefinition// 是RootBeanDefinition的子类ConfigurationClassBeanDefinition beanDef =         new ConfigurationClassBeanDefinition(configClass, metadata, beanName);beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));if (metadata.isStatic()) {// static @Bean methodif (configClass.getMetadata() instanceof StandardAnnotationMetadata) {beanDef.setBeanClass(                ((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());} else {beanDef.setBeanClassName(configClass.getMetadata().getClassName());}beanDef.setUniqueFactoryMethodName(methodName);} else {// instance @Bean methodbeanDef.setFactoryBeanName(configClass.getBeanName());beanDef.setUniqueFactoryMethodName(methodName);}if (metadata instanceof StandardMethodMetadata) {beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());}beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);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);// Consider scopingScopedProxyMode 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 necessaryBeanDefinition 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);}this.registry.registerBeanDefinition(beanName, beanDefToRegister);}

@ComponentScan注解

支持@ComponentScan注解的最终逻辑在ClassPathScanningCandidateComponentProvider类的scanCandidateComponents方法中:

private Set scanCandidateComponents(String basePackage) {Set candidates = new LinkedHashSet<>();try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + "/" + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (resource.isReadable()) {try {// 此处获取到的是SimpleMetadataReader对象,// 内部使用ASM解析.class文件封装AnnotationMetadata对象MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);// 判断是一个Componentif (isCandidateComponent(metadataReader)) {// 创建ScannedGenericBeanDefinition对象ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);if (isCandidateComponent(sbd)) {candidates.add(sbd);}}} catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}}} catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;}
上一篇 下一篇

相关新闻

spring启动流程 (3) BeanDefinition详解-全球焦点

录取通知书里的中国式浪漫,祝福考生

世界信息:欧佩克在沙特减产之前6月份石油产量小幅下降

半程马拉松一般人时间 成绩_半程马拉松一般人时间

潮白河顺义段38公里即将通航

骑马与砍杀2多人模式有哪些 骑马与砍杀2联机pve 当前热点

当前观点:闽南海蛎炒瘦肉怎样做?

耶和华是谁?答案_耶和华是谁

context什么意思(context)-环球观察

天下武尊下载_天下武尊-环球资讯

小米手机发布会时间|时讯

诺基亚920多少钱一台(诺基亚920多少钱)-全球最新

每日观点:石家庄市维明路小学举行2023届学生毕业典礼

最难绕口令广西壮族自治区_最难绕口令-世界简讯

速讯:毕业季租房市场“旺季不旺”,租金未出现明显上涨

最新新闻

spring启动流程 (3) BeanDefinition详解-全球焦点

录取通知书里的中国式浪漫,祝福考生

世界信息:欧佩克在沙特减产之前6月份石油产量小幅下降

半程马拉松一般人时间 成绩_半程马拉松一般人时间

潮白河顺义段38公里即将通航

骑马与砍杀2多人模式有哪些 骑马与砍杀2联机pve 当前热点

当前观点:闽南海蛎炒瘦肉怎样做?

耶和华是谁?答案_耶和华是谁

context什么意思(context)-环球观察

天下武尊下载_天下武尊-环球资讯

小米手机发布会时间|时讯

诺基亚920多少钱一台(诺基亚920多少钱)-全球最新

每日观点:石家庄市维明路小学举行2023届学生毕业典礼

最难绕口令广西壮族自治区_最难绕口令-世界简讯

速讯:毕业季租房市场“旺季不旺”,租金未出现明显上涨

速看:一文读懂RS-232、RS-422及RS-485串行数据标准的选择及使用

改掉坏习惯-坏习惯例子 全球速讯

“港车北上”正式实施,首批香港车主踏上大湾区便利之路|今日精选

logistic回归中or值的意义(or值的意义)

我家族里的财富,经历了两次大洗牌,一次是土改,二次是改革开放_全球焦点

苹果正计划推出适用于AirPods Pro的USB-C充电盒|焦点信息

广东荔枝“鄙视链”:桂味、糯米糍、仙进奉……都比妃子笑有排面

今日视点:形象思维和抽象思维的区别与联系(形象思维和抽象思维的区别)

今日现货白银价格最新查询(7月3日)|今日快看

潜江检察支持起诉,老人的医疗费有着落了!

今日报丨去西藏旅游什么时候比较好_去西藏旅游什么时候去最好

叙利亚驻华大使:世界上最富有的国家,光天化日下偷窃我们的石油和粮食

菲律宾官方旅游宣传片出现印尼、泰国风景?菲旅游局紧急下架并展开调查 天天亮点

热点!2023安徽芜湖市公安局辅警招聘笔试公告

厦门会展好戏连台 多场展会7月登场 世界聚焦