Spring 源码(13)Spring Bean 的创建过程(4)
Spring Bean
的创建过程非常的复杂,上一篇重点介绍了Spring
在创建Bean
的过程中,使用InstantiationBeanPostProcessor
进行提前创建Bean
,我们可以通过CGLIB
创建对象对Bean
的方法进行增强,当然也可以进行其他方式的创建方式。通过提前创建Bean
,减少了调用doCreateBean
方法的复杂逻辑的执行,而且通过这种方式可以定制创建的方式,便于扩展。
使用 supplier 进行Bean的提前暴露
接下来继续介绍Spring
的创建过程,执行doCreateBean
方法:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 实例化对象
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 省略代码....
}
这里会先从缓存中获取FactoryBean
实例化的对象,如果有就进行下面的逻辑,一般来说基本是获取不到的,就会走下面创建createBeanInstance
方法。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
// 解析Bean Class 用于创建对象
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 判断class必须是public修饰的,否则报错
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
// 获取到supplier,如果不为空,则创建对象直接返回
// 扩展点,可以在这里进行对象的初始化创建,使用BFPP对BeanDefinition进行设置supplier
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 使用FactoryMethod进行对象的创建
// 扩展点
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// 省略部分代码....
}
我们可以看到这里两个return
,意味着只要获取到Bean
,那么就不需要进行下一步的执行,首先看getInstanceSupplier
,这个是BeanDefinition中的方法,那说明可以在解析BeanDefinition
的时候进行处理,那么什么时候进行BeanDefinition
的扩展解析呢?根据前面的介绍可以得知在解析BeanFactoryPostProcessor
时可以进行BeanDefinition
的处理。
那为啥不是loadBeanDefinition
时处理呢?因为Spring
在加载阶段是没有提供扩展点的,而在BeanFactoryPostProcessor
接口注册和执行的时候,完全是可以自己定义一个BeanFactoryPostProcessor
进行扩展实现。
这个属性位于AbstractBeanDefinition
类中,一般来说用户自定义的BeanDefinition
都是GenericBeanDefinition
,而GenericBeanDefinition
是继承这个抽象类的,所以我们在进行BFPP
扩展实现时可以对GenericBeanDefinition
设置这个属性值,这个属性值是一个Supplier
函数式接口,相当于lambda
表达式的用法,接下来自己实现一个验证一下。
创建一个SupplierUser对象:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class SupplierUser {
private String username;
public SupplierUser() {
}
public SupplierUser(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "SupplierUser{" +
"username='" + username + '\'' +
'}';
}
}
创建一个创建SupplierUser的类:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class CreateSupplier {
public static SupplierUser createUser(){
return new SupplierUser("redwinter");
}
}
创建BFPP的实现:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class SupplierBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("supplierUser");
// 获取原生的BeanDefinition
GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) beanDefinition;
// 实例化Supplier
genericBeanDefinition.setInstanceSupplier(CreateSupplier::createUser);
// 设置类型
genericBeanDefinition.setBeanClass(CreateSupplier.class);
}
}
xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="supplierUser" class="com.redwinter.test.supplier.SupplierUser"/>
<bean class="com.redwinter.test.supplier.SupplierBeanFactoryPostProcessor"/>
</beans>
测试类:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class SupplierTest {
/**
* 使用BFPP设置Supplier进行对象的创建
* BFPP可以对BeanDefinition进行设置和修改
*/
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("supplier.xml");
SupplierUser bean = ac.getBean(SupplierUser.class);
System.out.println(bean);
}
}
当xml
中不配置BFPP
的时候:
输出:
SupplierUser{username='null'}
如果配置了BFPP
输出:
SupplierUser{username='redwinter'}
说明Bean
的创建的过程中通过Supplier
进行了提前的创建。
接下来看下一个扩展点:
FactoryMethod 对象的创建
根据源码可以看出这个属性也是在BeanDefinition
中的,但是这个可以通过标签的方式进行设置,在Spring
中factory-method
创建Bean
有两种方式,一种是静态工厂创建,一种是实例工厂创建。
接下来实验一下:
创建电视类,这个就是需要创建的Bean
对象:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class Tv {
private String name;
private String age;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Tv{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
创建静态类用于静态工厂创建bean
:
/**
* 家电类
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class StaticJiaDian {
public static Tv getTv(String name){
Tv tv = new Tv();
tv.setName(name);
tv.setAge("15");
return tv;
}
}
创建实例类,用于实例工厂创建对象:
/**
* 家电类
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class JiaDian {
public Tv getTv(String name){
Tv tv = new Tv();
tv.setName(name);
tv.setAge("13");
return tv;
}
}
xml
配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--静态工厂创建对象-->
<bean id="tv" class="com.redwinter.test.factorymethod.StaticJiaDian" factory-method="getTv">
<constructor-arg>
<value type="java.lang.String">海尔</value>
</constructor-arg>
</bean>
<!--实例工厂-->
<bean class="com.redwinter.test.factorymethod.JiaDian" id="jiaDian"/>
<bean id="tv2" class="com.redwinter.test.factorymethod.Tv" factory-bean="jiaDian" factory-method="getTv">
<constructor-arg>
<value type="java.lang.String">美的</value>
</constructor-arg>
</bean>
</beans>
测试类:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class FactoryMethodTest {
/**
* factory-method 对象的创建方式
* 静态工厂创建方式: 直接使用静态工厂类进行创建
* 实例工厂创建方式: 需要配合FactoryBean进行创建
*/
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("factory-method.xml");
Tv tv = ac.getBean("tv", Tv.class);
System.out.println(tv);
Tv tv2 = ac.getBean("tv2", Tv.class);
System.out.println(tv2);
}
}
输出:
Tv{name='海尔', age='15'}
Tv{name='美的', age='13'}
说明确实是调用了我们自定义的方法创建的对象。
总结下目前来说Bean的创建方式有:
- 使用FactoryBean创建
- 使用InstantiationAwreBeanPostProcessor的前置实例化方法postProcessBeforeInstantiation进行创建
- 使用Supplier进行创建
- 使用factory-method标签进行创建
- 实例工厂创建(配合factory-bean标签)
- 静态工厂创建
- 反射创建(常规的,完整的创建流程)
本篇就介绍到这里,下一篇继续介绍Bean
的创建流程。
Spring 源码(13)Spring Bean 的创建过程(4)的更多相关文章
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- Spring 源码分析之 bean 依赖注入原理(注入属性)
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
- Spring源码-IOC部分-Bean实例化过程【5】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
- dubbo源码分析3-service bean的创建与发布
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- Spring源码分析专题 —— IOC容器启动过程(上篇)
声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...
- 【Spring源码分析】Bean加载流程概览(转)
转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...
- Spring源码分析:Bean加载流程概览及配置文件读取
很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...
- 【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
- Spring 源码分析之 bean 实例化原理
本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...
- 初探Spring源码之Spring Bean的生命周期
写在前面的话: 学无止境,写博客纯粹是一种乐趣而已,把自己理解的东西分享出去,不意味全是对的,欢迎指正! Spring 容器初始化过程做了什么? AnnotationConfigApplication ...
随机推荐
- memcached 最大的优势是什么?
Memcached 最大的好处就是它带来了极佳的水平可扩展性,特别是在一个巨大的 系统中.由于客户端自己做了一次哈希,那么我们很容易增加大量 memcached 到集群中.memcached 之间没有 ...
- 如何从https://developer.mozilla.org上查询对象的属性、方法、事件使用说明和示例
在https://developer.mozilla.org搜索要在前面加上指令 搜索之后点进去 进入之后就是这样的 在页面左边你可以选择自己要查询的对象 里面就是会有属性.方法.事件使用说明和示例.
- 2.安装Spark与Python练习
一.安装Spark <Spark2.4.0入门:Spark的安装和使用> 博客地址:http://dblab.xmu.edu.cn/blog/1307-2/ 1.1 基础环境 1.1.1 ...
- 祖先元素transform非none时在Iphone6上引起后代fixed/absolute元素的怪异表现及解决方案
如题,祖先元素transform非none时,记录一下Iphone6中引起后代元素fixed参考视图怪异表现和解决方案. 层叠关系及参考视图 层叠上下文是HTML元素的三维概念,这些HTML元素在一条 ...
- 用原生js写一个"多动症"的简历
用原生js写一个"多动症"的简历 预览地址源码地址 最近在知乎上看到@方应杭用vue写了一个会动的简历,觉得挺好玩的,研究一下其实现思路,决定试试用原生js来实现. 会动的简历实现 ...
- HTML 和 form 表单常用标签
HTML和CSS 常用标签: p:段落,自动换行 span:和div类似,但是默认不换行 br:换行 hr:分割线 h1-h6:标题标签 a:超链接 瞄点:通过给a链接设置#XX作为链接,给需要链接的 ...
- Java自定义异常类的简单实现
学习目标: 掌握自定义异常类 例题: 需求:自定义异常类,简单判断是否注册成功 代码如下: RegisterException类: /** * @author YanYang * @projectNa ...
- Java基础之浅谈接口
前言 前几篇文章我们已经把Java的封装.继承.多态学习完了,现在我们开始比较便于我们实际操作的学习,虽然它也是Java基础部分,但是其实入门容易,精通很难. 我认真的给大家整理了一下这些必须学会.了 ...
- Spring Boot-@Configuration注解
@Configuration:指明当前类是一个配置类,就是来替代spring的配置文件 @Configuration public class MyConfigFile { @Bean public ...
- For-Each循环(增强型For循环)
public class Demo077 { public static void main(String[] args) { int[] array ={11,2}; System.out.prin ...