SpringBoot中将@Bean方法解析为BeanDefinition详解

 更新时间:2023年12月05日 10:32:29   作者:流烟默  
这篇文章主要介绍了SpringBoot中将@Bean方法解析为BeanDefinition详解,得到的BeanDefinition是ConfigurationClassBeanDefinition类型,会为BeanDefinition设置factoryMethodName,这意味着当实例化这个bean的时候将采用工厂方法,需要的朋友可以参考下

SpringBoot如何将@Bean方法注册为BeanDefinition

我们以MybatisPlusAutoConfiguration为例说明sqlSessionFactory()这个方法如何解析为BeanDefinition。

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
//...
}    

这个解析过程是在ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForBeanMethod方法。

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
	ConfigurationClass configClass = beanMethod.getConfigurationClass();
	MethodMetadata metadata = beanMethod.getMetadata();
	//本文这里是sqlSessionFactory
	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;
	}
//获取到@Bean的注解信息
	AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
	Assert.state(bean != null, "No @Bean annotation attributes");
	// Consider name and any aliases
	List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
	//尝试获取bean的名称,默认使用methodName
	String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
	// Register aliases even when overridden
	//尝试注册别名
	for (String alias : names) {
		this.registry.registerAlias(beanName, alias);
	}
	// Has this effectively been overridden before (e.g. via XML)?
	//校验BeanDefinition是否存在、是否允许覆盖
	if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
		if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
			throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
					beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
					"' clashes with bean name for containing configuration class; please make those names unique!");
		}
		return;
	}
//得到一个ConfigurationClassBeanDefinition 
	ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
	beanDef.setResource(configClass.getResource());
	beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
//方法是否为静态方法
	if (metadata.isStatic()) {
		// static @Bean method
		if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
			beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
		}
		else {
			beanDef.setBeanClassName(configClass.getMetadata().getClassName());
		}
		beanDef.setUniqueFactoryMethodName(methodName);
	}
	else {
	//实例方法,非静态方法
		// instance @Bean method
		//设置FactoryBeanName,本文这里是
		//com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
		beanDef.setFactoryBeanName(configClass.getBeanName());
		// 设置factoryMethodName 本文这里是sqlSessionFactory
		beanDef.setUniqueFactoryMethodName(methodName);
	}
//本文这里跳过 这里是SimpleMethodMetadata
	if (metadata instanceof StandardMethodMetadata) {
		beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
	}
//设置注入标识  AUTOWIRE_CONSTRUCTOR=3
	beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
	//设置SKIP_REQUIRED_CHECK_ATTRIBUTE属性
	beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
			SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
//BeanDefinition的通用后置处理,如Lazy、Primary、DependsOn、Role以及Description
	AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
//解析注解上配置的autowire
	Autowire autowire = bean.getEnum("autowire");
	if (autowire.isAutowire()) {
		beanDef.setAutowireMode(autowire.value());
	}
//解析注解上配置的autowireCandidate
	boolean autowireCandidate = bean.getBoolean("autowireCandidate");
	if (!autowireCandidate) {
		beanDef.setAutowireCandidate(false);
	}
//解析注解上配置的initMethod
	String initMethodName = bean.getString("initMethod");
	if (StringUtils.hasText(initMethodName)) {
		beanDef.setInitMethodName(initMethodName);
	}
//解析注解上配置的destroyMethod
	String destroyMethodName = bean.getString("destroyMethod");
	beanDef.setDestroyMethodName(destroyMethodName);
	// Consider scoping
	ScopedProxyMode proxyMode = ScopedProxyMode.NO;
	//获取@Scope注解信息
	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);
	}
	if (logger.isTraceEnabled()) {
		logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
				configClass.getMetadata().getClassName(), beanName));
	}
	//注册BeanDefinition,beanName默认是methodName sqlSessionFactory
	this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

方法流程如上所示,核心步骤都加了注释。这里要特别注意的是如下几点:

  • 得到的BeanDefinition是ConfigurationClassBeanDefinition类型;
  • 会为BeanDefinition设置factoryMethodName,这意味着当实例化这个bean的时候将采用工厂方法;
  • 会区分方法是否为静态方法来设置BeanClass或者FactoryBeanName

这里得到的configClass

在这里插入图片描述

这里得到的MethodMetadata

在这里插入图片描述

最终得到的BeanDefinition属性

annotationMetadata = {SimpleAnnotationMetadata@5352} 
factoryMethodMetadata = {SimpleMethodMetadata@5635} 
decoratedDefinition = null
qualifiedElement = null
stale = false
allowCaching = true
isFactoryMethodUnique = true
targetType = null
resolvedTargetType = null
isFactoryBean = null
factoryMethodReturnType = null
factoryMethodToIntrospect = null
constructorArgumentLock = {Object@5823} 
resolvedConstructorOrFactoryMethod = null
constructorArgumentsResolved = false
resolvedConstructorArguments = null
preparedConstructorArguments = null
postProcessingLock = {Object@5824} 
postProcessed = false
beforeInstantiationResolved = null
externallyManagedConfigMembers = null
externallyManagedInitMethods = null
externallyManagedDestroyMethods = null
beanClass = null
scope = ""
abstractFlag = false
lazyInit = null
autowireMode = 3
dependencyCheck = 0
dependsOn = null
autowireCandidate = true
primary = false
qualifiers = {LinkedHashMap@5825}  size = 0
instanceSupplier = null
nonPublicAccessAllowed = true
lenientConstructorResolution = false
factoryBeanName = "com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration"
factoryMethodName = "sqlSessionFactory"
constructorArgumentValues = null
propertyValues = null
methodOverrides = {MethodOverrides@5826} 
initMethodName = null
destroyMethodName = "(inferred)"
enforceInitMethod = true
enforceDestroyMethod = true
synthetic = false
role = 0
description = null
resource = {ClassPathResource@5353} "class path resource [com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.class]"
source = {SimpleMethodMetadata@5635} 
attributes = {LinkedHashMap@5827}  size = 1

到此这篇关于SpringBoot中将@Bean方法解析为BeanDefinition详解的文章就介绍到这了,更多相关@Bean方法解析为BeanDefinition内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java通过Excel批量上传数据的实现示例

    java通过Excel批量上传数据的实现示例

    Excel批量上传是常见的一种功能,本文就来介绍一下java通过Excel批量上传数据的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • Java线程池队列DelayQueue的使用详解

    Java线程池队列DelayQueue的使用详解

    这篇文章主要介绍了Java线程池队列DelayQueue的使用详解,DelayQueue顾名思义,它是个无边界延迟队列,它的底层是基于PriorityBlockingQueue实现的,该队列中的元素都是按照过期时间顺序排序的,队列头部放的是即将过期的元素,需要的朋友可以参考下
    2023-12-12
  • SpringBoot实现Server-Sent Events(SSE)的使用完整指南

    SpringBoot实现Server-Sent Events(SSE)的使用完整指南

    使用SpringBoot实现Server-Sent Events(SSE)可以有效处理实时数据推送需求,具有单向通信、轻量级和高实时性等优势,本文详细介绍了在SpringBoot中创建SSE端点的步骤,并通过代码示例展示了客户端如何接收数据,适用于实时通知、数据展示和在线聊天等场景
    2024-09-09
  • 基于SpringBoot实现邮箱找回密码的代码示例

    基于SpringBoot实现邮箱找回密码的代码示例

    本文主要介绍了如何基于SpringBoot实现邮箱找回密码,文中通过代码示例给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-02-02
  • MyBatis自定义映射关系和关联查询实现方法详解

    MyBatis自定义映射关系和关联查询实现方法详解

    这篇文章主要介绍了MyBatis自定义映射关系和关联查询实现方法,当POJO属性名与数据库列名不一致时,需要自定义实体类和结果集的映射关系,在MyBatis注解开发中,使用@Results定义并使用自定义映射,使用 @ResultMap使用自定义映射
    2023-04-04
  • java之Object类用法实例

    java之Object类用法实例

    这篇文章主要介绍了java之Object类用法实例,是Java程序设计中比较重要的概念,以实例的形式对其定义及用法做了较为深入的讲解,需要的朋友可以参考下
    2014-10-10
  • Java中的Sentinel持久化规则启动

    Java中的Sentinel持久化规则启动

    这篇文章主要介绍了Java中的Sentinel持久化规则启动,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • 浅谈Java内部类——静态内部类

    浅谈Java内部类——静态内部类

    这篇文章主要介绍了Java静态内部类的相关资料,帮助大家更好的理解和学习Java内部类的相关知识,感兴趣的朋友可以了解下
    2020-08-08
  • 浅析Mybatis Plus和Mybatis的区别

    浅析Mybatis Plus和Mybatis的区别

    这篇文章主要介绍了Mybatis Plus和Mybatis的区别,需要的朋友可以参考下
    2020-08-08
  • java读取文件:char的ASCII码值=65279,显示是一个空字符的解决

    java读取文件:char的ASCII码值=65279,显示是一个空字符的解决

    这篇文章主要介绍了java读取文件:char的ASCII码值=65279,显示是一个空字符的解决,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08

最新评论