Spring 源码之Spring Bean 的创建过程

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标签)
    • 静态工厂创建
  • 反射创建(常规的,完整的创建流程)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值