亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring的FactoryBean有什么作用

發布時間:2021-12-07 13:42:05 來源:億速云 閱讀:136 作者:iii 欄目:大數據

這篇文章主要講解了“Spring的FactoryBean有什么作用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Spring的FactoryBean有什么作用”吧!

1.一個 Demo

我們先從一個簡單的 Demo 開始。

新建一個 Maven 項目,引入 Spring 依賴,然后創建一個 HelloService,如下:

public class HelloService {
    public String hello() {
        return "hello javaboy";
    }
}
 

然后再創建一個 HelloService 的工廠類:

public class HelloServiceFactoryBean implements FactoryBean<HelloService> {
    @Override
    public HelloService getObject() throws Exception {
        return new HelloService();
    }

    @Override
    public Class<?> getObjectType() {
        return HelloService.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
 

我們新建的 HelloServiceFactoryBean 類實現了 FactoryBean 接口,并指定了 HelloService 泛型。

接下來我們在 beans.xml 文件中配置 HelloServiceFactoryBean 實例:

<?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 class="org.javaboy.springdemo03.HelloServiceFactoryBean" id="helloService"/>
</beans>
 

加載該配置文件:

@Test
void contextLoads() {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
    Object helloService = ctx.getBean("helloService");
    System.out.println(helloService.getClass().toString());
}
 

加載 XML 配置文件,并獲取 Bean 實例,然后將 Bean 實例的類型打印出來。

按照我們之前的理解,這里獲取到的 Bean 應該是 HelloServiceFactoryBean 的實例,但是實際打印結果如下:

class org.javaboy.springdemo03.HelloService
 

竟然是 HelloService!

到底怎么回事?我們得從 FactoryBean 接口開始講起。

 

2.FactoryBean 接口

FactoryBean 在 Spring 框架中具有重要地位,Spring 框架本身也為此提供了多種不同的實現類:

Spring的FactoryBean有什么作用  

按理說我們配置一個 Bean,直接在 XML 文件中進行配置即可。但是有的時候一個類的屬性非常多,在 XML 中配置起來反而非常不方便,這個時候 Java 代碼配置的優勢就體現出來了,使用 FactoryBean 就可以通過 Java 代碼配置 Bean 的相關屬性。

從 Spring3.0 開始,FactoryBean 開始支持泛型,就是大家所看到的 FactoryBean形式,FactoryBean接口中一共有三個方法:

public interface FactoryBean<T> {
 @Nullable
 T getObject() throws Exception;
 @Nullable
 Class<?> getObjectType();
 default boolean isSingleton() {
  return true;
 }

}
 
  • getObject:該方法返回 FactoryBean 所創建的實例,如果在 XML 配置文件中,我們提供的 class 是一個 FactoryBean 的話,那么當我們調用 getBean 方法去獲取實例時,最終獲取到的是 getObject 方法的返回值。
  • getObjectType:返回對象的類型。
  • isSingleton:getObject 方法所返回的對象是否單例。

所以當我們調用 getBean 方法去獲取 Bean 實例的時候,實際上獲取到的是 getObject 方法的返回值,那么是不是就沒有辦法獲取 HelloServiceFactoryBean 呢?當然是有的!

@Test
void contextLoads() {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
    Object helloService = ctx.getBean("&helloService");
    System.out.println(helloService.getClass().toString());
}
 

打印結果如下:

class org.javaboy.springdemo03.HelloServiceFactoryBean
 

小伙伴們可以看到,只需要在 Bean 的名字前面加上一個 & 符號,獲取到的就是 HelloServiceFactoryBean 實例了。

 

3.源碼分析

根據 Spring 源碼第六彈!松哥和大家聊聊容器的始祖 DefaultListableBeanFactory 一文中的介紹,在 DefaultListableBeanFactory 中還有一個 preInstantiateSingletons 方法可以提前注冊 Bean,該方法是在 ConfigurableListableBeanFactory 接口中聲明的,DefaultListableBeanFactory 類實現了 ConfigurableListableBeanFactory 接口并實現了接口中的方法:

@Override
public void preInstantiateSingletons() throws BeansException {
 if (logger.isTraceEnabled()) {
  logger.trace("Pre-instantiating singletons in " + this);
 }
 // 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<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
 // Trigger initialization of all non-lazy singleton beans...
 for (String beanName : beanNames) {
  RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
  if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
   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<Boolean>)
          ((SmartFactoryBean<?>) factory)::isEagerInit,
        getAccessControlContext());
     }
     else {
      isEagerInit = (factory instanceof SmartFactoryBean &&
        ((SmartFactoryBean<?>) factory).isEagerInit());
     }
     if (isEagerInit) {
      getBean(beanName);
     }
    }
   }
   else {
    getBean(beanName);
   }
  }
 }
 // Trigger post-initialization callback for all applicable beans...
 for (String beanName : beanNames) {
  Object singletonInstance = getSingleton(beanName);
  if (singletonInstance instanceof SmartInitializingSingleton) {
   final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
   if (System.getSecurityManager() != null) {
    AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
     smartSingleton.afterSingletonsInstantiated();
     return null;
    }, getAccessControlContext());
   }
   else {
    smartSingleton.afterSingletonsInstantiated();
   }
  }
 }
}
 

preInstantiateSingletons 方法的整體邏輯比較簡單,就是遍歷 beanNames,對符合條件的 Bean 進行實例化,而且大家注意,這里所謂的提前初始化其實就是在我們調用 getBean 方法之前,它自己先調用了一下 getBean。

這里有幾個比較關鍵的點。

第一個就是 isFactoryBean 方法的調用,該方法就是根據 beanName 去獲取 Bean 實例,進而判斷是不是一個 FactoryBean,如果沒有 Bean 實例(還沒創建出來),則根據 BeanDefinition 去判斷是不是一個 FactoryBean。

如果是 FactoryBean,則在 getBean 時自動加上了 FACTORY_BEAN_PREFIX 前綴,這個常量其實就是 &,這樣獲取到的實例實際上就是 FactoryBean 的實例。獲取到 FactoryBean 實例之后,接下來判斷是否需要在容器啟動階段,調用 getObject 方法初始化 Bean,如果 isEagerInit 為 true,則去初始化。

按照我們前面的定義,這里獲取到的 isEagerInit 屬性為 false,即不提前加載 Bean,而是在開發者手動調用 getBean 的方法的時候才去加載。如果希望這里能夠提前加載,需要重新定義 HelloServiceFactoryBean,并使之實現 SmartFactoryBean 接口,如下:

public class HelloServiceFactoryBean2 implements SmartFactoryBean<HelloService> {
    @Override
    public HelloService getObject() throws Exception {
        return new HelloService();
    }

    @Override
    public Class<?> getObjectType() {
        return HelloService.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public boolean isEagerInit() {
        return true;
    }
}
 

實現了 SmartFactoryBean 接口之后,重寫 isEagerInit 方法并返回 true,其他方法不變,重新配置 beans.xml 文件,然后啟動容器。此時在容器啟動時,就會提前調用 getBean 方法完成 Bean 的加載。

接下來我們來看 getBean 方法。

getBean 方法首先調用 AbstractBeanFactory#doGetBean,在該方法中,又會調用到 AbstractBeanFactory#getObjectForBeanInstance 方法:

protected Object getObjectForBeanInstance(
  Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
 // Don't let calling code try to dereference the factory if the bean isn't a factory.
 if (BeanFactoryUtils.isFactoryDereference(name)) {
  if (beanInstance instanceof NullBean) {
   return beanInstance;
  }
  if (!(beanInstance instanceof FactoryBean)) {
   throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
  }
  if (mbd != null) {
   mbd.isFactoryBean = true;
  }
  return beanInstance;
 }
 // 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.
 if (!(beanInstance instanceof FactoryBean)) {
  return beanInstance;
 }
 Object object = null;
 if (mbd != null) {
  mbd.isFactoryBean = true;
 }
 else {
  object = getCachedObjectForFactoryBean(beanName);
 }
 if (object == null) {
  // Return bean instance from factory.
  FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
  // Caches object obtained from FactoryBean if it is a singleton.
  if (mbd == null && containsBeanDefinition(beanName)) {
   mbd = getMergedLocalBeanDefinition(beanName);
  }
  boolean synthetic = (mbd != null && mbd.isSynthetic());
  object = getObjectFromFactoryBean(factory, beanName, !synthetic);
 }
 return object;
}
 

這段源碼很有意思:

BeanFactoryUtils.isFactoryDereference 方法用來判斷 name 是不是以 & 開頭的,如果是以 & 開頭的,表示想要獲取的是一個 FactoryBean,那么此時如果 beanInstance 剛好就是一個 FactoryBean,則直接返回。并將 mbd 中的 isFactoryBean 屬性設置為 true。

如果 name 不是以 & 開頭的,說明用戶就是想獲取 FactoryBean 所構造的 Bean,那么此時如果 beanInstance 不是 FactoryBean 實例,則直接返回。

如果當前的 beanInstance 是一個 FactoryBean,而用戶想獲取的只是一個普通 Bean,那么就會進入到接下來的代碼中。

首先調用 getCachedObjectForFactoryBean 方法去從緩存中獲取 Bean。如果是第一次獲取 Bean,這個緩存中是沒有的數據的,getObject 方法調用過一次之后,Bean 才有可能被保存到緩存中了。

為什么說有可能呢?Bean 如果是單例的,則會被保存在緩存中,Bean 如果不是單例的,則不會被保存在緩存中,而是每次加載都去創建新的。

如果沒能從緩存中加載到 Bean,則最終會調用 getObjectFromFactoryBean 方法去加載 Bean。

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);
      }
     }
     if (containsSingleton(beanName)) {
      this.factoryBeanObjectCache.put(beanName, object);
     }
    }
   }
   return object;
  }
 }
 else {
  Object object = doGetObjectFromFactoryBean(factory, beanName);
  if (shouldPostProcess) {
   try {
    object = postProcessObjectFromFactoryBean(object, beanName);
   }
   catch (Throwable ex) {
    throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
   }
  }
  return object;
 }
}
 

在 getObjectFromFactoryBean 方法中,首先通過 isSingleton 和 containsSingleton 兩個方法判斷 getObject 方法返回值是否是單例的,單例的走一條路,非單例的走另外一條路。

如果是單例的:

先去緩存中再拿一次,看能不能拿到。如果緩存中沒有,調用 doGetObjectFromFactoryBean 方法去獲取,這是真正的獲取方法。獲取到之后,進行 Bean 的后置處理,處理完成后,如果 Bean 是單例的,就緩存起來。

如果不是單例的:

不是單例就簡單,直接調用 doGetObjectFromFactoryBean 方法獲取 Bean 實例,然后進行后置處理就完事,也不用緩存。

接下來我們就來看看 doGetObjectFromFactoryBean 方法:

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
 Object object;
 try {
  if (System.getSecurityManager() != null) {
   AccessControlContext acc = getAccessControlContext();
   try {
    object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
   }
   catch (PrivilegedActionException pae) {
    throw pae.getException();
   }
  }
  else {
   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;
}
 

做了一些判斷之后,最終通過 factory.getObject(); 方法獲取我們想要的實例。

這就是整個 FactoryBean 的加載流程。

 

4.應用

FactoryBean 在 Spring 框架中有非常廣泛的應用,即使我們不寫上面那段代碼,也還是使用了不少的 FactoryBean。

接下來松哥隨便舉兩個例子。

 

4.1 SqlSessionFactoryBean

這可能是大家接觸最多的 FactoryBean,當 MyBatis 整合 Spring 時,我們少不了如下一行配置:

<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="typeAliasesPackage" value="org.javaboy.meeting.model"/>
    <property name="mapperLocations">
        <value>
            classpath*:org/javaboy/meeting/mapper/*.xml
        </value>
    </property>
</bean>
 

這就是在配置 FactoryBean。當我們單獨使用 MyBatis 時候,需要有一個 SqlSessionFactory,現在整合之后,沒有 SqlSessionFactory 了,我們有理由相信是 SqlSessionFactoryBean 的 getObject 方法提供了 SqlSessionFactory,我們來看下其源碼:

@Override
public SqlSessionFactory getObject() throws Exception {
  if (this.sqlSessionFactory == null) {
    afterPropertiesSet();
  }
  return this.sqlSessionFactory;
}
 

確實如此!

 

4.2 Jackson2ObjectMapperFactoryBean

這也是一個大家使用相對較多的 FactoryBean。

如果項目中使用了 Jackson,同時希望在全局層面做一些配置,一般來說我們可能會用到這個類:

<mvc:annotation-driven conversion-service="conversionService">
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="simpleDateFormat" value="yyyy-MM-dd"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>
 

MappingJackson2HttpMessageConverter 類的 objectMapper 屬性實際上是需要一個 ObjectMapper 對象,但是我們這里卻提供了一個 Jackson2ObjectMapperFactoryBean,這是因為 Jackson2ObjectMapperFactoryBean 的 getObject 方法就是我們需要的 ObjectMapper:

@Override
@Nullable
public ObjectMapper getObject() {
 return this.objectMapper;
}
   

4.3 FormattingConversionServiceFactoryBean

FormattingConversionServiceFactoryBean 也曾經出現在松哥的 SpringMVC 教程中(公眾號江南一點雨后臺回復 springmvc 獲取教程)。

如果前端傳遞的參數格式是 key-value 形式,那么日期類型的參數需要服務端提供一個日期類型轉換器,像下面這樣:

@Component
public class DateConverter implements Converter<String, Date> {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    public Date convert(String source) {
        try {
            return sdf.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}
 

然后在 XML 文件中對此進行配置:

<mvc:annotation-driven conversion-service="conversionService"/>
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="conversionService">
    <property name="converters">
        <set>
            <ref bean="dateConverter"/>
        </set>
    </property>
</bean>
 

FormattingConversionServiceFactoryBean 中的 getObject 方法最終返回的是 FormattingConversionService。

類似的例子還有很多,例如 EhCacheManagerFactoryBean、YamlPropertiesFactoryBean 等。

感謝各位的閱讀,以上就是“Spring的FactoryBean有什么作用”的內容了,經過本文的學習后,相信大家對Spring的FactoryBean有什么作用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

岗巴县| 习水县| 库尔勒市| 天门市| 东海县| 黔南| 德安县| 新民市| 周至县| 汶上县| 青冈县| 罗定市| 石嘴山市| 武夷山市| 冕宁县| 高陵县| 江津市| 临安市| 崇阳县| 舞阳县| 柳江县| 汕头市| 涟水县| 宜黄县| 泾阳县| 蓬溪县| 酒泉市| 乐至县| 大新县| 内黄县| 宣城市| 扬州市| 汉阴县| 武夷山市| 博野县| 勐海县| 滨州市| 石台县| 辉南县| 延边| 法库县|