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中的,但是这个可以通过标签的方式进行设置,在Springfactory-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的创建流程。

最新文章

  1. 编写高质量代码:改善Java程序的151个建议(第8章:多线程和并发___建议126~128)
  2. c++宏定义命令
  3. TCP/IP协议与UDP协议的区别
  4. Android studio debug模式获取变量的值
  5. List 源码分析笔记
  6. 游戏 window
  7. asp.net ueditor 图片上传路径问题解决
  8. zoj3839-Poker Face
  9. java笔记13之成员变量与局部变量
  10. php中switch语句case后表达式写法记录一
  11. How to create DB2 user function easily by DB Query Analyzer 6.03
  12. headfirst设计模式(7)—命令模式
  13. MacBook PyCharm激活码(附视频)
  14. spring创建bean的三种方式
  15. MyBatis-Cache
  16. centos7下apache2.4反向代理
  17. css3实现卷页效果http://jingyan.baidu.com/article/73c3ce2806aef9e50343d93a.html
  18. CentOS 6.8 curl支持的NSS修改为OpenSSL的方法
  19. postman的巨坑 之 cookie
  20. [HNOI2018]转盘

热门文章

  1. 学习GlusterFS(七)
  2. Effective Java —— 消除过期的对象引用
  3. scrapy 如何链接有密码的redis scrapy-redis 设置redis 密码 scrapy-redis如何为redis配置密码
  4. C语言 | 栈的应用 | 非递归栈实现快排
  5. PAT B1002写出这个数
  6. 小程序tab栏可滑动,可点击居中demo
  7. video踩坑
  8. ES 架构及基础 - 1
  9. /dev/dm-0 ....(/dev/mapper机制)
  10. 【.NET6+Modbus】Modbus TCP协议解析、仿真环境以及基于.NET实现基础通信