Spring Bean
的创建过程非常的复杂,上一篇重点介绍了Spring
在创建Bean
的过程中,使用InstantiationBeanPostProcessor
进行提前创建Bean
,我们可以通过CGLIB
创建对象对Bean
的方法进行增强,当然也可以进行其他方式的创建方式。通过提前创建Bean
,减少了调用doCreateBean
方法的复杂逻辑的执行,而且通过这种方式可以定制创建的方式,便于扩展。
接下来继续介绍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
进行了提前的创建。
接下来看下一个扩展点:
根据源码可以看出这个属性也是在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的创建方式有:
本篇就介绍到这里,下一篇继续介绍Bean
的创建流程。