文章目录
系列文章主旨
从如何把自己的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