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中的,但是这个可以通过标签的方式进行设置,在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标签)
    • 静态工厂创建
  • 反射创建(常规的,完整的创建流程)

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

本篇就介绍到这里,下一篇继续介绍Bean的创建流程。

发表评论

相关文章