您的位置:首页 > 编程语言 > Java开发

Spring 源码(13)Spring Bean 的创建过程(4)

2022-05-12 09:25 225 查看 https://www.cnblogs.com/redwin

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
    的创建流程。

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: