Spring容器实例化Bean底层原理
-
Spring容器在初始化时,会将xml中配置的<bean>的信息(并不是bean对象本身,而是其信息)封装成一个BeanDefinition对象
-
再创建一个名为beanDefinitionMap的Map集合用于存储容器内所有的BeanDefinition对象
-
Spring框架对beanDefinitionMap进行遍历,并通过反射机制创建对应的Bean对象
-
创建一个名为singletonObjects的Map集合用于存储产生的Bean对象
-
当调用getBean方法时则从singletonObjects中获取对应的Bean并返回
Spring的后处理器
Spring的后处理器允许*第三方介入*到Bean的整个实例化流程中,方便实现动态注册BeanDefinition、修改BeanDefinition以及修改Bean的效果,是Spring对外开放的重要扩展点
- 注册BeanDefinition:理解为往BeanDefinitionMap添加BeanDefinition的过程
本质:在Spring对Bean实例化的过程中,其是依靠遍历BeanDefinitionMap中的每一个BeanDefinition而进行相应的Bean实例化,而这里的思路就是通过操纵(添加、修改、删除等)往BeanDefinitionMap中添加的某个BeanDefinition,便可以修改其实例化的Bean
Spring主要的两种处理器:
-
BeanFactoryPostProcessor
Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行
BeanFactoryPostProcessor是一个接口,将实现了该接口的类交予Spring容器进行管理,则Spring会自动调用接口方法,用于实现对BeanDefinition注册和修改的功能(主要偏向于对注册Bean的功能)
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}
例子-不通过配置将Processor放入BeanDefinitionMap中:
前提:Processor并未注册
将MyBeanFactoryPostProcessor交予Spring容器管理
<bean class="com.xiaowan.processor.MyBeanFactoryPostProcessor">
</bean>```
```java
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryPostProcessor启动");
//创建一个BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
//BeanDefinition注入Processor类路径
beanDefinition.setBeanClassName("com.xiaowan.dao.impl.ProcessorImpl");
//DefaultListableBeanFactory继承了ConfigurableListableBeanFactory接口,功能更加强大,通过其registerBeanDefinition方法将新创建的BeanDefinition放入BeanDefinitionMap中
DefaultListableBeanFactory defaultBeanFactory = (DefaultListableBeanFactory) beanFactory;
defaultBeanFactory.registerBeanDefinition("processorDao",beanDefinition);
}
}
以上仅为案例,在实际开发中当需要通过此方法进行注册时,可以使用BeanFactoryPostProcessor的子接口BeandefinitionRegistryPostProcessor,其专门用于注册BeanDefinition
其中在BeandefinitionRegistryPostProcessor的实现类的
postProcessBeanDefinitionRegistry
方法中进行注册操作
-
BeanPostProcessor
Bean后处理器,在Bean实例化之后,填充到单例池singletonObjects之前执行(中间会经历Bean的初始化过程,例如属性的填充、初始化init等等)
BeanPostProcessor是一个接口,将实现了该接口的类交予Spring容器进行管理,则Spring会自动调用接口方法,实现对Bean对象的操作(主要偏向于对实初始化的Bean对象本身进行操作的功能)
BeanPostProcessor中存在两个接口方法: -
postProcessBeforeInitialization:before
-
postProcessAfterInitialization:after
执行时机:
graph TB
A(Bean实例化) --> B(postProcessBeforeInitialization)
B --> C(属性设置)
C --> D(init方法执行)
D --> E(postProcessAfterInitialization);
注解配置 实现的基本思路
大概思路,实际注解的实现非常复杂,实现思想与本讲解抽象起来差不多
在Bean工厂的后处理器中,创建一个解析器,对指定包的整个包中的类进行全方位代码扫描,获取使用了指定注解的类的相关信息(例如类名,类路径),存储到BeanDefinitionMap中,成功实现注解注册Bean
例--自定义注解MyComponent完成Spring托管配置
MyComponent注解的声明:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyComponent { String value(); }
Bean工厂的后处理器--BeanDefinitionRegistryPostProcessor的实现类
public class MyComponentBeanFactoryProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { //通过扫描工具扫描指定包及其子包下的所有类,收集使用@MyComponent的注解的类 Map<String, Class> myComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation("com.xiaowan"); //遍历Map,组装BeanDefinition注册 myComponentAnnotationMap.forEach((beanName,clazz)-> { //获得beanClassName String beanClassName = clazz.getName(); //创建BeanDefinition BeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClassName(beanClassName); //注册 beanDefinitionRegistry.registerBeanDefinition(beanName,beanDefinition); }); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { } }
将BeanDefinitionRegistryPostProcessor交予spring托管
<bean class="com.xiaowan.test.MyComponentBeanFactoryProcessor"> </bean>
使用注解
@MyComponent("otherBean") public class OtherBean { }
自定义注解配置成功实现
解析器具体代码
public class BaseClassScanUtils { //设置资源规则 private static final String RESOURCE_PATTERN = "/**/*.class"; public static Map<String, Class> scanMyComponentAnnotation(String basePackage) { //创建容器存储使用了指定注解的Bean字节码对象 Map<String, Class> annotationClassMap = new HashMap<String, Class>(); //spring工具类,可以获取指定路径下的全部类 ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); try { String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN; Resource[] resources = resourcePatternResolver.getResources(pattern); //MetadataReader 的工厂类 MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver); for (Resource resource : resources) { //用于读取类信息 MetadataReader reader = refractory.getMetadataReader(resource); //扫描到的class String classname = reader.getClassMetadata().getClassName(); Class<?> clazz = Class.forName(classname); //判断是否属于指定的注解类型 if(clazz.isAnnotationPresent(MyComponent.class)){ //获得注解对象 MyComponent annotation = clazz.getAnnotation(MyComponent.class); //获得属value属性值 String beanName = annotation.value(); //判断是否为"" if(beanName!=null&&!beanName.equals("")){ //存储到Map中去 annotationClassMap.put(beanName,clazz); continue; } //如果没有为"",那就把当前类的类名作为beanName annotationClassMap.put(clazz.getSimpleName(),clazz); } } } catch (Exception exception) { } return annotationClassMap; } }
Spring流程图Spring Bean的生命周期
即Bean的实例化(通过反射创建出对象)-> Bean的初始化(Bean成为完整对象)-> 存储到单例池中
三个阶段:
graph LR
A[Bean的实例化阶段]-->B[Bean的初始化阶段]-->C[Bean的完成阶段]
- Bean的实例化阶段
graph LR
A[Spring框架对BeanDefinition的信息进行判断]-->B{"是否是singleton的"}
A-->C{"是否延迟加载"}
A-->D{"是否是FactoryBean"}
A-->E{"......"}
F[将一个普通的singleton的Bean通过反射进行实例化]
B-->F
C-->F
D-->F
E-->F
-
Bean的初始化阶段
实例化阶段得到的Bean为半成品,还需进行加工
graph TB
A[填充Bean实例的属性]-->B[执行部分Aware接口方法]
-->C[执行BeanPostProcessor方法]-->D[执行InitializingBean接口的初始化方法]
-->E[执行自定义初始化init方法]-->F[......]
该阶段是Spring最具有技术含量和复杂度的阶段
例如:
Aop增强功能
Spring注解功能
Bead的循环引用问题等等
-
Bean的完成阶段
初始化完成后,Bean成功编程Spring Bean,并存入单例池
Spring Bean的初始化阶段
-
Bean实例的属性填充
在Beandefinition中propertyValues用于存储Bean实体的注入信息
不同数据类型的属性填充方式:
-
注入普通属性、String、int或存储基本类型的集合
通过set方法的反射设置
-
注入单向对象引用属性 (单向:即A类需要注入B类,但B类不需要注入A类)
以下情况的前提是容器中载入了引用对象
-
当容器中存在引用对象时,从容器中getBean()获取后通过set方法反射设置
-
当容器中不存在引用对象时(即当前Bean实例已被创建时所需注入Bean实例还未被创建),则先创建被注入对象Bean实例(完成整个生命周期后),再进行注入
-
-
注入双向对象引用属性 (双向:即A类需要注入B类,且B类也需要注入A类)
循环依赖/循环引用 (重点原理)
多个实体之间相互依赖并形成闭环
-
graph LR
A[BeanA] --> B[BeanB]
B-->A
原先单向引用对象方法应用与双向引用对象场景场景:
注意点:
该图中,所检验的容器中是否存在Bean实例的真正含义是:是否能在单例池(SingletonObjects)中查找到该类,而这里的实例化Bean实例仅仅只是开辟内存空间存储半成品,还无法存入单例池中,即不能作为通过条件
以上过程不断循环,使得内存占用越来越多,并且无法解决问题
开拓思路:
当实例化BeanA后,进入其引用类的判断,接着实例化BeanB,再次进入其引用类的判断,回到BeanA的实例化...
为了使得死循环拥有终点:
这里将实例化好的BeanA存入一个Map集合中(由于单例池只存储完整的Bean,这里使用一个新的池),当进行BeanB的实例化以及对应注入类时,先从单例池进行注入类的查询,当查询无果时,再从这个新池子进行注入类的查询,查询到不完整的BeanA后,先将不完整的BeanA注入至BeanB终结死循环,后续再对BeanA进行相应的操作使BeanA也完整化
以上思想即为三级缓存
三级缓存
为了可以存储完整Bean实例和半成品Bean实例解决循环引用问题,Spring框架创建出DefaultSingletonBeanRegistry类(DefaultListableBeanFactory的上四级父类)提供了三个池子实现三级缓存
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16); private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); ...... }
singletonObjects 一级缓存
最终存储单例Bean成品的容器(实例化和初始化都完成的Bean)
earlySingletonObjects 二级缓存
早期Bean单例池,缓存半成品对象且当前对象已经被其他对象引用
singletonFactories 三级缓存
单例Bean的工厂池,缓存半成品对象且对象未被引用
使用时通过工厂创建Bean
ObjectFactory:用于实现延迟依赖查找(区别于延迟加载等)
查找底层:依赖于beanFactory.getBean()
延迟底层:依赖于DependencyObjectProvider,只有当我们调用ObjectFactory.getBean()方法才会对所需依赖进行处理
场景引入:
BeanA进行实例化后,Spring框架依靠半成品的BeanA创建一个对应的ObjectFactory对象(包装一层,当实际需要半成品对象时,调用ObjectFactory对象的getObject方法返回对象实例用于使用)并放入三级缓存中(singletonFactories)
进入BeanA注入类的查询(在三个池子中依次进行进行查询),并未查询到BeanB后,创建出BeanB的半成品对象,并同BeanA半成品对象的方式创建对应的ObjectFactory对象其放入三级缓存中
在三个池子中依次查询BeanB的注入类BeanA,在三级缓存中查询到BeanA,将BeanA注入至BeanB,与此同时,将原先的BeanA从三级缓存中移除,并将BeanA实例放入二级缓存中
注入所需注入类后的BeanB执行其他生命周期过程,最终成为一个完成的Bean对象,并存入一级缓存中,同时删除二三级缓存
BeanA注入完成的BeanB,执行其他声明周期过程,最终成为完整的Bean,存入一级缓存中,并删除二三级缓存
图解:
后续对Bean的许多重要操作基本都依赖本过程