【Spring】Spring原理源码解析(四)-- Spring在解析配置类时,怎么识别并处理@Bean的

本文深入探讨Spring如何解析配置类并处理@Bean注解。从配置类的初始化、@Bean的三种使用场景(类方法、内部类方法、接口默认方法)的解析,到核心处理逻辑和源码解析,全面展示了Spring识别@Bean并将其注册到容器的过程。
摘要由CSDN通过智能技术生成

系列文章主旨

从如何把自己的Bean注册到Spring容器开始,从该点出发,每篇只关注一个核心流程的原理、源码,再由该篇带出引申出来的其他Spring知识点,继续剖析,最终达到理解Spring核心原理的目的;

上篇内容

分析了spring如何通过内置的,bean工厂后置处理器,解析配置类,识别配置类注解@Import,然后去遍历解析被导入的配置,最后生成bd,注册到spring容器中;
详情:【Spring】Spring原理源码解析(三)-- Spring在解析配置类时,怎么识别并处理@Import类的

本篇内容

从上一篇分析的spring内部实现的ConfigurationClassPostProcessor继续入手,分析spring在解析配置类的时候,又是怎么识别@Bean的原理源码;

文章图片均出自:
@Bean识别原理图解

核心原理

代码示例

@Bean一般使用,在类方法上定义

扫包,初始化容器,然后通过spring容器获取A类

// 普通@Bean解析
 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
 context.scan("com.zsh.demo.spring.learn.register.bean.normalBean");
 context.refresh();

 A aBean = context.getBean(A.class);
 System.out.println(aBean);
 // 普通@Bean解析

A类实际上就是通过@Bean方法注入到容器中的

/**
 * @description: 在方法上定义@Bean
 */
@Component
public class NormalBean {
   

    @Bean
    public A createA() {
   
        return new A();
    }

}
@Bean在内部类方法上定义

扫包,初始化容器,然后通过spring容器获取A类

// 内部类解析
  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  context.scan("com.zsh.demo.spring.learn.register.bean.memberBean");
  context.refresh();

  OuterBean.MemberClz memberClz = context.getBean(OuterBean.MemberClz.class);
  System.out.println(memberClz);
  A aBean = context.getBean(A.class);
  System.out.println(aBean);
  // 内部类解析

A类实际上是在内部类的@Bean方法注入到容器中的

/**
 * @description: 内部类注册
 */
@Component
public class OuterBean {
   

    public class MemberClz {
   
        @Bean
        public A createA() {
   
            return new A();
        }
    }

}
@Bean在接口默认方法上定义(比较不常用)

扫包,初始化容器,然后通过spring容器获取A类和B类

// 接口@Bean解析
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.zsh.demo.spring.learn.register.bean.interfaceBean");
context.refresh();

ImplementInterfaceClz implementInterfaceClz = context.getBean(ImplementInterfaceClz.class);
System.out.println(implementInterfaceClz);
A aBean = context.getBean(A.class);
System.out.println(aBean);
B bBean = context.getBean(B.class);
System.out.println(bBean);
// 接口@Bean解析

A类是在接口默认方法上通过@Bean注入的,实现类实现了接口

@Component
public class ImplementInterfaceClz implements InterfaceBean {
   
}
/**
* 接口上定义默认方法
*/
public interface InterfaceBean extends InterfaceBean2 {
   

    @Bean
    default A createA() {
   
        return new A();
    }

}

紧接着接口InterfaceBean继承了InterfaceBean2,B类就是在InterfaceBean2接口上通过@Bean注入的
所以,接口默认方法@Bean的方式注入bean,是会遍历继承关系去找的

public interface InterfaceBean2 {
   

    @Bean
    default B createB() {
   
        return new B();
    }

}

图解

在这里插入图片描述

核心处理逻辑

Spring容器进行初始化时,会默认生成自己的bean工厂后置处理器ConfigurationClassPostProcessor,并注册到spring容器中,然后在执行所有的bean工厂后置处理时,就会执行到ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法;

在执行该方法时,会解析我们注册到Spring的配置类,本篇解析的是@Bean注解的处理;

首先按照三种使用场景,分别进行下面的解析,获得@Bean注解标注的方法集合:

1、类上方法直接使用@Bean:这种会直接在解析配置类时,通过获取类中带有@Bean的方法;

2、接口默认方法使用@Bean:这种会在解析配置类时,先通过获得当前类的所有接口,然后获得接口上的@Bean方法,之后近这递归获得接口父类,同样获得@Bean方法,最终得到所有继承接口的@Bean方法;

3、内部类中的方法使用@Bean:这种会在解析配置类时,先获得所有内部类,遍历,每一个内部类会校验是否属于配置类(即带有@Component、@ComponentScan、@Configuration、@Import、@ImportResource、@Bean),带有的话,则把这些类也当作一个配置类,去递归解析,那么这些配置类有会再次走【1、2】的步骤,得到@Bean方法集合;

之后得到的@Bean方法,都会记录在配置类的属性beanMethods中,当某一批配置类解析完了,就会通过遍历这些配置类,然后拿到beanMethods,构造出来BeanDefinition,注册到Spring容器中;

进阶源码解析

spring调用内置工厂,处理配置类,定位到解析@Bean的入口

内部的工厂后置处理器ConfigurationClassPostProce

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值