1. 程式人生 > >聊聊Spring的FactoryBean其實沒那麼難

聊聊Spring的FactoryBean其實沒那麼難

## 前言 談到Spring的FactoryBean,就會知道Spring中經典的面試題:FactoryBean和BeanFactory的區別。我們這裡就簡單概括下: 、 1. BeanFactory是介面,提供了OC容器最基本的形式,給具體的IOC容器的實現提供了規範,FactoryBean也是介面,為IOC容器中Bean的實現提供了更加靈活的方式,FactoryBean在IOC容器的基礎上給Bean的實現加上了一個簡單工廠模式和裝飾模式(如果想了解裝飾模式參考:[修飾者模式(裝飾者模式,Decoration)](https://www.cnblogs.com/aspirant/p/9083082.html) 我們可以在getObject()方法中靈活配置。其實在Spring原始碼中有很多FactoryBean的實現類. 2. BeanFactory是個Factory,也就是IOC容器或物件工廠,FactoryBean是個Bean。在Spring中,**所有的Bean都是由BeanFactory(也就是IOC容器)來進行管理的**。但對FactoryBean而言,**這個Bean不是簡單的Bean,而是一個能生產或者修飾物件生成的工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式類似** 。 ## 示例 首先我們定義個普通的java pojo物件名叫Car ```java public class Car { private int maxSpeed ; private String brand ; private double price ; public double getPrice() { return price; } public int getMaxSpeed() { return maxSpeed; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return "Car{" + "maxSpeed=" + maxSpeed + ", brand='" + brand + '\'' + ", price=" + price + '}'; } } ``` 再定義一個實現FactoryBean介面的實現類CarFactoryBean ```java public class CarFactoryBean implements FactoryBean { private String carInfo; public Car getObject() throws Exception { System.out.println("start new car"); Car car = new Car(); String[] infos = carInfo.split(","); car.setBrand(infos[0]); car.setMaxSpeed(Integer.valueOf(infos[1])); car.setPrice(Double.valueOf(infos[2])); return car; } public Class getObjectType(){ return Car.class ; } //返回的例項是同一個 public boolean isSingleton() { return true; } public String getCarInfo() { return this.carInfo ; } public void setCarInfo(String carInfo) { this.carInfo = carInfo; } } ``` 再來看看我們的Xml配置是如何配置Bean的 ```xml
``` 最後我們載入這個配置檔案,啟動Spring容器來獲取car這個Bean ```java public static void main( String[] args ) { ClassPathXmlApplicationContext ac =new ClassPathXmlApplicationContext(); ac.setConfigLocations("spring-config-locatorfactory.xml"); ac.refresh(); //getBean的時候才會去getObject ac.getBean("car", Car.class); } ``` 成功打印出 ```java start new car Car{maxSpeed=400, brand='超級跑車', price=2000000.0} ``` ## 原理 如果我們把```ac.getBean("car", Car.class);```這行程式碼去掉,我們會發現start new car這行是不會列印的,那就是說明**getObject是延遲載入的,只有顯示呼叫才能觸發,spring在初始化啟動單例bean例項化的時候並不會觸發(SmartFactoryBean除外,之後我們會單獨分析下SmartFactoryBean)**。 那我們就到原始碼級別證明下Spring的FactoryBean的getObject是延遲初始化的 ### DefaultListableBeanFactory #### preInstantiateSingletons ```java @Override public void preInstantiateSingletons() throws BeansException { // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... //todo 遍歷所有不是延遲初始化的 單例 bean 2020-08-28 for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); //不是抽象的 且是單例的 且不是懶載入的 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //todo 如果是factorybean 那還得去判斷 是不是eagerInit if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean factory = (FactoryBean) bean; //判斷是不是渴望初始化的。。 boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction) ((SmartFactoryBean) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { getBean(beanName); } } } } ``` 我們看到Spring會在例項化單例Bean的時候會去判斷是否是Factorybean,如果是的話就會去根據```FACTORY_BEAN_PREFIX + beanName```獲取Factorybean本身,然後再判斷這個FactoryBean是SmartFactoryBean且isEagerInit設定為true才會去獲取beanName對應的Bean,也就是例項化這個bean。 那既然是延遲初始化的,那我們再獲取實際bean的時候Spring內部是怎麼做的呢? ### AbstractBeanFactory #### doGetBean ```java protected T doGetBean(final String name, @Nullable final Class requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // Create bean instance. //從頭開始載入 bean,這個過程由 getSingleton() 實現。 if (mbd.isSingleton()) { //https://www.cnblogs.com/leihuazhe/p/9481018.html ////todo 如果有單例那直接返回了 , args引數也就沒啥用處了 2020-08-30 sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly(顯式) remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } } ``` 這裡我們從getObjectForBeanInstance方法可以看出來這裡就是獲取真正bean的方法。 #### getObjectForBeanInstance ```java protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // Now we have the bean instance, which may be a normal bean or a FactoryBean. // If it's a FactoryBean, we use it to create a bean instance, unless the // caller actually wants a reference to the factory. //我們使用它來建立一個bean例項,除非呼叫者實際上想要引用工廠 //todo 如果是獲取FactoryBean本身 那就直接返回這個工廠 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } } ``` 也就是說不是FactoryBean本身或者根據name判斷就是獲取FactoryBean本身的話,那就直接返回這個beanInstance。 那我們在```getBean("car")```的時候其實獲取到的beanInstance是FactoryBean的例項,所以走到這邊的時候不會返回。 下面Spring就會走到抽象類FactoryBeanRegistrySupport中的getObjectFromFactoryBean函式,因為AbstractBeanFactory繼承自FactoryBeanRegistrySupport,從名字就可以看出這個函式的意義就是呼叫FactoryBean的getObject方法。 ### FactoryBeanRegistrySupport #### getObjectFromFactoryBean ```java /** * Obtain an object to expose from the given FactoryBean. * @param factory the FactoryBean instance * @param beanName the name of the bean * @param shouldPostProcess whether the bean is subject to post-processing * @return the object obtained from the FactoryBean * @throws BeanCreationException if FactoryBean object creation failed * @see org.springframework.beans.factory.FactoryBean#getObject() */ protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) { if (factory.isSingleton() && containsSingleton(beanName)) { synchronized (getSingletonMutex()) { Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { object = doGetObjectFromFactoryBean(factory, beanName); // Only post-process and store if not put there already during getObject() call above // (e.g. because of circular reference processing triggered by custom getBean calls) Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { if (shouldPostProcess) { if (isSingletonCurrentlyInCreation(beanName)) { // Temporarily(暫時) return non-post-processed object, not storing it yet.. return object; } beforeSingletonCreation(beanName); try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex); } finally { afterSingletonCreation(beanName); } } //object放入factoryBeanObjectCache快取 if (containsSingleton(beanName)) { this.factoryBeanObjectCache.put(beanName, object); } } } return object; } } else { //非單例 省略部分程式碼 } } ``` 函式內部還會呼叫doGetObjectFromFactoryBean函式 #### doGetObjectFromFactoryBean ```java /** * Obtain an object to expose from the given FactoryBean. * @param factory the FactoryBean instance * @param beanName the name of the bean * @return the object obtained from the FactoryBean * @throws BeanCreationException if FactoryBean object creation failed * @see org.springframework.beans.factory.FactoryBean#getObject() */ private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName) throws BeanCreationException { Object object; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { object = AccessController.doPrivileged((PrivilegedExceptionAction) factory::getObject, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //最重要的呼叫getObject方法 object = factory.getObject(); } } catch (FactoryBeanNotInitializedException ex) { throw new BeanCurrentlyInCreationException(beanName, ex.toString()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex); } // Do not accept a null value for a FactoryBean that's not fully // initialized yet: Many FactoryBeans just return null then. if (object == null) { if (isSingletonCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException( beanName, "FactoryBean which is currently in creation returned null from getObject"); } object = new NullBean(); } return object; } ``` 從這個函式中我們看到了FactoryBean自身的getObject方法。呼叫完getObject方法後,Spring就會把這個物件放入factoryBeanObjectCache快取物件中。之後再獲取的時候直接從factoryBeanObjectCache快取中拿就是了。 迎搜尋關注我的公眾號:碼農約翰的沉思錄,瞭解更多Spring原理 ![](https://img2020.cnblogs.com/blog/64521/202012/64521-20201215091810314-344815092.jpg)