当前位置: 首页 > news >正文

一文看懂@Bean注解的原理

在spring中可以通过配置类的标注@Bean注解的方法生成bean,这是常见的创建bean的方式之一,用法很简单,那他的原理是怎样的呢。首先处理配置类检测到方法存在@Bean注解时,就会提取bean方法。先反射获取获取bean方法,再asm获取bean方法,asm处理class就是处理原始的class字节码文件,然后把反射获取的bean方法按照asm获取的进行排序,目的是保证bean方法的有序性,因为有时候如果bean方法存在条件注解,需要先解析没有条件注解的bean方法,才能让条件生效。

	private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {AnnotationMetadata original = sourceClass.getMetadata();//反射获取bean方法Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {try {AnnotationMetadata asm =this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();//asm获取bean方法Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());//按照asm获取顺序进行排序if (asmMethods.size() >= beanMethods.size()) {Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());for (MethodMetadata asmMethod : asmMethods) {for (MethodMetadata beanMethod : beanMethods) {if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {selectedMethods.add(beanMethod);break;}}}if (selectedMethods.size() == beanMethods.size()) {// All reflection-detected methods found in ASM method set -> proceedbeanMethods = selectedMethods;}}}catch (IOException ex) {logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);}}return beanMethods;}

然后开始根据@Bean注解生成bean定义注册到容器中,这个过程有点复杂。

  1. 根据条件注解判断bean是否需要处理,如果不需要处理,则重载的同名bean方法也无需处理
  2. 处理同名bean方法
  3. 创建bean定义注册到容器中,设置bean的反射调用方式、代理模式等
	private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {ConfigurationClass configClass = beanMethod.getConfigurationClass();MethodMetadata metadata = beanMethod.getMetadata();String methodName = metadata.getMethodName();//根据条件注解标记当前配置类的bean方法是否跳过if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {configClass.skippedBeanMethods.add(methodName);return;}//被标记跳过的bean方法无需处理if (configClass.skippedBeanMethods.contains(methodName)) {return;}AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);Assert.state(bean != null, "No @Bean annotation attributes");// Consider name and any aliasesList<String> 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);}// 发现同名bean方法的处理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;}//创建bean定义注册到容器中ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));//static方法根据class调用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);}//实例方法根据name获取到实力对象再反射调用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);//处理@Lazy、@Primary、@DependsOn、@Role等注解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;}}// 是否生成代理对象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));}this.registry.registerBeanDefinition(beanName, beanDefToRegister);}

遇到同名bean时,处理的过程就是不存在bean定义就创建;配置类相同,bean方法名相同,则标记为方法不唯一,无须再次处理,之后创建bean的时候会调用多次。

	protected boolean isOverriddenByExistingDefinition(BeanMethod beanMethod, String beanName) {//不存在bean定义就创建if (!this.registry.containsBeanDefinition(beanName)) {return false;}BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;if (ccbd.getMetadata().getClassName().equals(beanMethod.getConfigurationClass().getMetadata().getClassName())) {if (ccbd.getFactoryMethodMetadata().getMethodName().equals(ccbd.getFactoryMethodName())) {//配置类相同,bean方法名相同,则标记为方法不唯一,无须再次处理ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());}return true;}else {return false;}}//同名的bean是注解创建的,则覆盖if (existingBeanDef instanceof ScannedGenericBeanDefinition) {return false;}// Has the existing bean definition bean marked as a framework-generated bean?// -> allow the current bean method to override it, since it is application-levelif (existingBeanDef.getRole() > BeanDefinition.ROLE_APPLICATION) {return false;}// 容器不允许覆盖bean定义抛出异常if (this.registry instanceof DefaultListableBeanFactory &&!((DefaultListableBeanFactory) this.registry).isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);}if (logger.isDebugEnabled()) {logger.debug(String.format("Skipping bean definition for %s: a definition for bean '%s' " +"already exists. This top-level bean definition is considered as an override.",beanMethod, beanName));}return true;}

总结下,spring处理@Bean方法时会先判断条件是否跳过,再处理同名方法,设置bean的反射调用方式以及代理模式。有不对的地方请大神指出,欢迎大家一起讨论交流,共同进步,更多请关注微信公众号 葡萄开源

http://www.xdnf.cn/news/1368469.html

相关文章:

  • 【C++】用哈希表封装实现unordered_set和unordered_map
  • Ubuntu 操作系统
  • 自动化测试概念与 Web 自动化实战(基于 Selenium)
  • Tensor常见操作
  • pycharm 远程连接服务器报错
  • Java基础第二课:hello word
  • 160.在 Vue3 中用 OpenLayers 解决国内 OpenStreetMap 地图加载不出来的问题
  • 从行业智能体到一站式开发平台,移动云推动AI智能体规模化落地
  • Windows 命令行:mkdir 命令
  • 三菱FX5U PLC访问字变量的某一位
  • Elasticsearch精准匹配与全文检索对比
  • 如何从零开始学习黑客技术?网络安全入门指南
  • 读《精益数据分析》:用户行为热力图
  • 【算法--链表题2】19.删除链表的倒数第 N 个节点:通俗详解
  • 腾讯开源OpenTenBase深度实践:企业级分布式HTAP数据库部署全攻略
  • Qt数据结构与编码技巧全解析
  • Spring - 文件上传与下载:真正的企业开发高频需求——Spring Boot文件上传与下载全场景实践指南
  • 基于stm32的物联网OneNet火灾报警系统
  • 支持向量机(SVM)内容概述
  • Hive高阶函数之行转列JSON数据解析
  • uniapp 引入使用u-view 完整步骤,u-view 样式不生效
  • 要闻集锦|阿里官网调整为四大业务板块;华为云重组多个事业部涉及上千人;群核科技在港交所更新招股书
  • 开源 python 应用 开发(十三)AI应用--百度智能云TTS语音合成
  • vscode 配置 + androidStudio配置
  • uniapp 自动升级-uni-upgrade-center
  • 复盘一个诡异的Bug之FileNotFoundException
  • 【实时Linux实战系列】实时信号处理在通信中的应用
  • leetcode-python-383赎金信
  • 为什么选择爱普生TG5032CFN温补晶振—可穿戴设备?
  • MATLAB Figure画布中绘制表格详解